本文主要聊一下spring security的permitAll以及webIgnore的区别

permitAll配置实例

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
                .authorizeRequests()
                .antMatchers("/css/**", "/js/**","/fonts/**").permitAll()
                .anyRequest().authenticated();
}复制代码

web ignore配置实例

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**");
        web.ignoring().antMatchers("/js/**");
        web.ignoring().antMatchers("/fonts/**");
}复制代码

二者区别

顾名思义,WebSecurity主要是配置跟web资源相关的,比如css、js、images等等,但是这个还不是本质的区别,关键的区别如下:

  • ingore是完全绕过了spring security的所有filter,相当于不走spring security
  • permitall没有绕过spring security,其中包含了登录的以及匿名的。
  • AnonymousAuthenticationFilter

    spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java

    * Detects if there is no { @code Authentication} object in the * { @code SecurityContextHolder}, and populates it with one if needed. * @author Ben Alex * @author Luke Taylor public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean { //...... public void doFilter (ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { if (SecurityContextHolder.getContext().getAuthentication() == null ) { SecurityContextHolder.getContext().setAuthentication( createAuthentication((HttpServletRequest) req)); if (logger.isDebugEnabled()) { logger.debug( "Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext().getAuthentication() + "'" ); else { if (logger.isDebugEnabled()) { logger.debug( "SecurityContextHolder not populated with anonymous token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'" ); chain.doFilter(req, res); protected Authentication createAuthentication (HttpServletRequest request) { AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken (key, principal, authorities); auth.setDetails(authenticationDetailsSource.buildDetails(request)); return auth; //...... } 复制代码

    这个filter的主要功能就是给没有登陆的用户,填充AnonymousAuthenticationToken到SecurityContextHolder的Authentication,后续依赖Authentication的代码可以统一处理。

    FilterComparator

    spring-security-config-4.1.4.RELEASE-sources.jar!/org/springframework/security/config/annotation/web/builders/FilterComparator.java

    final class FilterComparator implements Comparator<Filter>, Serializable {
        private static final int STEP = 100;
        private Map<String, Integer> filterToOrder = new HashMap<String, Integer>();
        FilterComparator() {
            int order = 100;
            put(ChannelProcessingFilter.class, order);
            order += STEP;
            put(ConcurrentSessionFilter.class, order);
            order += STEP;
            put(WebAsyncManagerIntegrationFilter.class, order);
            order += STEP;
            put(SecurityContextPersistenceFilter.class, order);
            order += STEP;
            put(HeaderWriterFilter.class, order);
            order += STEP;
            put(CorsFilter.class, order);
            order += STEP;
            put(CsrfFilter.class, order);
            order += STEP;
            put(LogoutFilter.class, order);
            order += STEP;
            put(X509AuthenticationFilter.class, order);
            order += STEP;
            put(AbstractPreAuthenticatedProcessingFilter.class, order);
            order += STEP;
            filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter",
                    order);
            order += STEP;
            put(UsernamePasswordAuthenticationFilter.class, order);
            order += STEP;
            put(ConcurrentSessionFilter.class, order);
            order += STEP;
            filterToOrder.put(
                    "org.springframework.security.openid.OpenIDAuthenticationFilter", order);
            order += STEP;
            put(DefaultLoginPageGeneratingFilter.class, order);
            order += STEP;
            put(ConcurrentSessionFilter.class, order);
            order += STEP;
            put(DigestAuthenticationFilter.class, order);
            order += STEP;
            put(BasicAuthenticationFilter.class, order);
            order += STEP;
            put(RequestCacheAwareFilter.class, order);
            order += STEP;
            put(SecurityContextHolderAwareRequestFilter.class, order);
            order += STEP;
            put(JaasApiIntegrationFilter.class, order);
            order += STEP;
            put(RememberMeAuthenticationFilter.class, order);
            order += STEP;
            put(AnonymousAuthenticationFilter.class, order);
            order += STEP;
            put(SessionManagementFilter.class, order);
            order += STEP;
            put(ExceptionTranslationFilter.class, order);
            order += STEP;
            put(FilterSecurityInterceptor.class, order);
            order += STEP;
            put(SwitchUserFilter.class, order);
        //......
    }复制代码

    这个类定义了spring security内置的filter的优先级,AnonymousAuthenticationFilter在倒数第五个执行,在FilterSecurityInterceptor这个类之前。

    FilterSecurityInterceptor

    spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/access/intercept/FilterSecurityInterceptor.java

    * Performs security handling of HTTP resources via a filter implementation. * The <code>SecurityMetadataSource</code> required by this security interceptor is of * type {@link FilterInvocationSecurityMetadataSource}. * Refer to {@link AbstractSecurityInterceptor} for details on the workflow. * @author Ben Alex * @author Rob Winch public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter { //...... } 复制代码

    这个相当于spring security的核心处理类了,它继承抽象类AbstractSecurityInterceptor

    spring-security-core-4.2.3.RELEASE-sources.jar!/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java

    public abstract class AbstractSecurityInterceptor implements InitializingBean,
            ApplicationEventPublisherAware, MessageSourceAware {
        //......
        protected InterceptorStatusToken beforeInvocation(Object object) {
            Assert.notNull(object, "Object was null");
            final boolean debug = logger.isDebugEnabled();
            if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
                throw new IllegalArgumentException(
                        "Security invocation attempted for object "
                                + object.getClass().getName()
                                + " but AbstractSecurityInterceptor only configured to support secure objects of type: "
                                + getSecureObjectClass());
            Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
                    .getAttributes(object);
            if (attributes == null || attributes.isEmpty()) {
                if (rejectPublicInvocations) {
                    throw new IllegalArgumentException(
                            "Secure object invocation "
                                    + object
                                    + " was denied as public invocations are not allowed via this interceptor. "
                                    + "This indicates a configuration error because the "
                                    + "rejectPublicInvocations property is set to 'true'");
                if (debug) {
                    logger.debug("Public object - authentication not attempted");
                publishEvent(new PublicInvocationEvent(object));
                return null; // no further work post-invocation
            if (debug) {
                logger.debug("Secure object: " + object + "; Attributes: " + attributes);
            if (SecurityContextHolder.getContext().getAuthentication() == null) {
                credentialsNotFound(messages.getMessage(
                        "AbstractSecurityInterceptor.authenticationNotFound",
                        "An Authentication object was not found in the SecurityContext"),
                        object, attributes);
            Authentication authenticated = authenticateIfRequired();
            // Attempt authorization
            try {
                this.accessDecisionManager.decide(authenticated, object, attributes);
            catch (AccessDeniedException accessDeniedException) {
                publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
                        accessDeniedException));
                throw accessDeniedException;
            if (debug) {
                logger.debug("Authorization successful");
            if (publishAuthorizationSuccess) {
                publishEvent(new AuthorizedEvent(object, attributes, authenticated));
            // Attempt to run as a different user
            Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
                    attributes);
            if (runAs == null) {
                if (debug) {
                    logger.debug("RunAsManager did not change Authentication object");
                // no further work post-invocation
                return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,
                        attributes, object);
            else {
                if (debug) {
                    logger.debug("Switching to RunAs Authentication: " + runAs);
                SecurityContext origCtx = SecurityContextHolder.getContext();
                SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
                SecurityContextHolder.getContext().setAuthentication(runAs);
                // need to revert to token.Authenticated post-invocation
                return new InterceptorStatusToken(origCtx, true, attributes, object);
        //......
    }复制代码

    主要的逻辑在这个beforeInvocation方法,它就依赖了authentication

    private Authentication authenticateIfRequired() {
            Authentication authentication = SecurityContextHolder.getContext()
                    .getAuthentication();
            if (authentication.isAuthenticated() && !alwaysReauthenticate) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Previously Authenticated: " + authentication);
                return authentication;
            authentication = authenticationManager.authenticate(authentication);
            // We don't authenticated.setAuthentication(true), because each provider should do
            // that
            if (logger.isDebugEnabled()) {
                logger.debug("Successfully Authenticated: " + authentication);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            return authentication;
        }复制代码

    这个方法判断authentication如果是已经校验过的,则返回;没有校验过的话,则调用authenticationManager进行鉴权。

    而AnonymousAuthenticationFilter设置的authentication在这个时候就派上用场了
    spring-security-core-4.2.3.RELEASE-sources.jar!/org/springframework/security/authentication/AnonymousAuthenticationToken.java
    ```
    public class AnonymousAuthenticationToken extends AbstractAuthenticationToken implements
    Serializable {
    private AnonymousAuthenticationToken(Integer keyHash, Object principal,
    Collection<? extends GrantedAuthority> authorities) {
    super(authorities);

        if (principal == null || "".equals(principal)) {
            throw new IllegalArgumentException("principal cannot be null or empty");
        Assert.notEmpty(authorities, "authorities cannot be null or empty");
        this.keyHash = keyHash;
        this.principal = principal;
        setAuthenticated(true);
    //......复制代码

    }
    ```

    它默认就是authenticated

  • web ignore比较适合配置前端相关的静态资源,它是完全绕过spring security的所有filter的;
  • 而permitAll,会给没有登录的用户适配一个AnonymousAuthenticationToken,设置到SecurityContextHolder,方便后面的filter可以统一处理authentication。
  • spring security 的几个细节
  • Spring Security – security none, filters none, access permitAll
  • Spring Security permitAll() not allowing anonymous access
  • Difference between access=“permitAll” and filters=“none”?
  • 分类:
    后端
    标签: