• SpringSecurity认证流程


    UsernamePasswordAuthenticationFilter
    先看主要负责认证的过滤器UsernamePasswordAuthenticationFilter,有删减,注意注释。

    public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter
    {
        public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
        public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
        private String usernameParameter = "username";
        private String passwordParameter = "password";
        private boolean postOnly = true;
        public UsernamePasswordAuthenticationFilter() {
            super(new AntPathRequestMatcher("/login", "POST"));
        }
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse
                response) throws AuthenticationException {
    //必须为POST请求
            if (this.postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " +
                        request.getMethod());
            } else {
                String username = this.obtainUsername(request);
                String password = this.obtainPassword(request);
                if (username == null) {
                    username = "";
                }
                if (password == null) {
                    password = "";
                }
                username = username.trim();
    //将填写的用户名和密码封装到了UsernamePasswordAuthenticationToken中
                UsernamePasswordAuthenticationToken authRequest = new
                        UsernamePasswordAuthenticationToken(username, password);
                this.setDetails(request, authRequest);
    //调用AuthenticationManager对象实现认证
                return this.getAuthenticationManager().authenticate(authRequest);
            }
        }
    }

    AuthenticationManager
    由上面源码得知,真正认证操作在AuthenticationManager里面!
    然后看AuthenticationManager的实现类ProviderManager:

    public class ProviderManager implements AuthenticationManager, MessageSourceAware,
            InitializingBean {
        private static final Log logger = LogFactory.getLog(ProviderManager.class);
        private AuthenticationEventPublisher eventPublisher;
        private List<AuthenticationProvider> providers;
        protected MessageSourceAccessor messages;
        private AuthenticationManager parent;
        private boolean eraseCredentialsAfterAuthentication;
        //注意AuthenticationProvider这个对象,SpringSecurity针对每一种认证,什么qq登录啊,
    //用户名密码登陆啊,微信登录啊都封装了一个AuthenticationProvider对象。
        public ProviderManager(List<AuthenticationProvider> providers) {
            this(providers, (AuthenticationManager)null);
        }
        public Authentication authenticate(Authentication authentication) throws
                AuthenticationException {
            Class<? extends Authentication> toTest = authentication.getClass();
            AuthenticationException lastException = null;
            AuthenticationException parentException = null;
            Authentication result = null;
            Authentication parentResult = null;
            boolean debug = logger.isDebugEnabled();
            Iterator var8 = this.getProviders().iterator();
    //循环所有AuthenticationProvider,匹配当前认证类型。
            while(var8.hasNext()) {
                AuthenticationProvider provider = (AuthenticationProvider)var8.next();
                if (provider.supports(toTest)) {
                    if (debug) {
                        logger.debug("Authentication attempt using " +
                                provider.getClass().getName());
                    }
                    try {
    //找到了对应认证类型就继续调用AuthenticationProvider对象完成认证业务。
                        result = provider.authenticate(authentication);
                        if (result != null) {
                            this.copyDetails(authentication, result);
                            break;
                        }
                    } catch (AccountStatusException var13) {
                        this.prepareException(var13, authentication);
                        throw var13;
                    } catch (InternalAuthenticationServiceException var14) {
                        this.prepareException(var14, authentication);
                        throw var14;
                    } catch (AuthenticationException var15) {
                        lastException = var15;
                    }
                }
            }
            if (result == null && this.parent != null) {
                try {
                    result = parentResult = this.parent.authenticate(authentication);
                } catch (ProviderNotFoundException var11) {
                } catch (AuthenticationException var12) {
                    parentException = var12;
                    lastException = var12;
                }
            }
            if (result != null) {
                if (this.eraseCredentialsAfterAuthentication && result instanceof
                        CredentialsContainer) {
                    ((CredentialsContainer)result).eraseCredentials();
                }
                if (parentResult == null) {
                    this.eventPublisher.publishAuthenticationSuccess(result);
                }
                return result;
            } else {
                if (lastException == null) {
                    lastException = new
                            ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new
                            Object[]{toTest.getName()}, "No AuthenticationProvider found for {0}"));
                }
                if (parentException == null) {
                    this.prepareException((AuthenticationException)lastException, authentication);
                }
                throw lastException;
            }
        }
    }

    AbstractUserDetailsAuthenticationProvider
    咱们继续再找到AuthenticationProvider的实现类AbstractUserDetailsAuthenticationProvider:

    public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
        private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
        private PasswordEncoder passwordEncoder;
        private volatile String userNotFoundEncodedPassword;
        private UserDetailsService userDetailsService;
        private UserDetailsPasswordService userDetailsPasswordService;
        protected final UserDetails retrieveUser(String username,
                                                 UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
            this.prepareTimingAttackProtection();
            try {
    //重点来了!主要就在这里了!
    //可别忘了,咱们为什么要翻源码,是想用自己数据库中的数据实现认证操作啊!
    //UserDetails就是SpringSecurity自己的用户对象。
    //this.getUserDetailsService()其实就是得到UserDetailsService的一个实现类
    //loadUserByUsername里面就是真正的认证逻辑
    //也就是说我们可以直接编写一个UserDetailsService的实现类,告诉SpringSecurity就可以了!
    //loadUserByUsername方法中只需要返回一个UserDetails对象即可
                UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
    //若返回null,就抛出异常,认证失败。
                if (loadedUser == null) {
                    throw new InternalAuthenticationServiceException("UserDetailsService returned
                    null, which is an interface contract violation");
                } else {
    //若有得到了UserDetails对象,返回即可。
                    return loadedUser;
                }
            } catch (UsernameNotFoundException var4) {
                this.mitigateAgainstTimingAttack(authentication);
                throw var4;
            } catch (InternalAuthenticationServiceException var5) {
                throw var5;
            } catch (Exception var6) {
                throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
            }
        }
    }

    AbstractUserDetailsAuthenticationProvider中authenticate返回值
    按理说到此已经知道自定义认证方法的怎么写了,但咱们把返回的流程也大概走一遍,上面不是说到返回了一个
    UserDetails对象对象吗?跟着它,就又回到了AbstractUserDetailsAuthenticationProvider对象中authenticate方
    法的最后一行了。

    public abstract class AbstractUserDetailsAuthenticationProvider implements
            AuthenticationProvider, InitializingBean, MessageSourceAware {
        public Authentication authenticate(Authentication authentication) throws
                AuthenticationException {
    //最后一行返回值,调用了createSuccessAuthentication方法,此方法就在下面!
            return this.createSuccessAuthentication(principalToReturn, authentication, user);
        }
        //咿!?怎么又封装了一次UsernamePasswordAuthenticationToken,开局不是已经封装过了吗?
        protected Authentication createSuccessAuthentication(Object principal, Authentication
                authentication, UserDetails user) {
    //那就从构造方法点进去看看,这才干啥了。
            UsernamePasswordAuthenticationToken result = new
                    UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(),
                    this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
            result.setDetails(authentication.getDetails());
            return result;
        }
    }

    UsernamePasswordAuthenticationToken
    来到UsernamePasswordAuthenticationToken对象发现里面有两个构造方法

    public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
        private static final long serialVersionUID = 510L;
        private final Object principal;
        private Object credentials;
        //认证成功前,调用的是这个带有两个参数的。
        public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
            super((Collection)null);
            this.principal = principal;
            this.credentials = credentials;
            this.setAuthenticated(false);
        }
        //认证成功后,调用的是这个带有三个参数的。
        public UsernamePasswordAuthenticationToken(Object principal, Object credentials,Collection<? extends GrantedAuthority> authorities) {
    //看看父类干了什么!
            super(authorities);
            this.principal = principal;
            this.credentials = credentials;
            super.setAuthenticated(true);
        }
    }

    AbstractAuthenticationToken
    再点进去super(authorities)看看:

    public abstract class AbstractAuthenticationToken implements Authentication,
            CredentialsContainer {
        private final Collection<GrantedAuthority> authorities;
        private Object details;
        private boolean authenticated = false;
        public AbstractAuthenticationToken(Collection<? extends GrantedAuthority> authorities) {
    //这时两个参数那个分支!
            if (authorities == null) {
                this.authorities = AuthorityUtils.NO_AUTHORITIES;
            } else {
    //三个参数的,看这里!
                Iterator var2 = authorities.iterator();
    //原来是多个了添加权限信息的步骤
                GrantedAuthority a;
                do {
                    if (!var2.hasNext()) {
                        ArrayList<GrantedAuthority> temp = new ArrayList(authorities.size());
                        temp.addAll(authorities);
                        this.authorities = Collections.unmodifiableList(temp);
                        return;
                    }
                    a = (GrantedAuthority)var2.next();
                } while(a != null);
    //若没有权限信息,是会抛出异常的!
                throw new IllegalArgumentException("Authorities collection cannot contain any null
                        elements");
            }
        }
    }

    由此,咱们需要牢记自定义认证业务逻辑返回的UserDetails对象中一定要放置权限信息啊!
    咱们回到最初的地方UsernamePasswordAuthenticationFilter,你看好看了,这可是个过滤器,咱们分析这么
    久,都没提到doFilter方法,你不觉得心里不踏实?可是这里面也没有 doFilter呀?那就从父类找!

    AbstractAuthenticationProcessingFilter
    点开AbstractAuthenticationProcessingFilter,删掉不必要的代码!

    public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
            implements ApplicationEventPublisherAware, MessageSourceAware {
        //doFilter再次!
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws
                IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest)req;
            HttpServletResponse response = (HttpServletResponse)res;
            if (!this.requiresAuthentication(request, response)) {
                chain.doFilter(request, response);
            } else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Request is to process authentication");
                }
                Authentication authResult;
                try {
                    authResult = this.attemptAuthentication(request, response);
                    if (authResult == null) {
                        return;
                    }
                    this.sessionStrategy.onAuthentication(authResult, request, response);
                } catch (InternalAuthenticationServiceException var8) {
                    this.logger.error("An internal error occurred while trying to authenticate the
                            user.", var8);
                    this.unsuccessfulAuthentication(request, response, var8);
                    return;
                } catch (AuthenticationException var9) {
                    this.unsuccessfulAuthentication(request, response, var9);
                    return;
                }
                if (this.continueChainBeforeSuccessfulAuthentication) {
                    chain.doFilter(request, response);
                }
                this.successfulAuthentication(request, response, chain, authResult);
            }
        }
        protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse
                response) {
            return this.requiresAuthenticationRequestMatcher.matches(request);
        }
        //成功走successfulAuthentication
        protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Authentication success. Updating SecurityContextHolder to
                        contain: " + authResult);
            }
    //认证成功,将认证信息存储到SecurityContext中!
            SecurityContextHolder.getContext().setAuthentication(authResult);
    //登录成功调用rememberMeServices
            this.rememberMeServices.loginSuccess(request, response, authResult);
            if (this.eventPublisher != null) {
                this.eventPublisher.publishEvent(new
                        InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
            }
            this.successHandler.onAuthenticationSuccess(request, response, authResult);
        }
        //失败走unsuccessfulAuthentication
        protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse
                response, AuthenticationException failed) throws IOException, ServletException {
            SecurityContextHolder.clearContext();
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Authentication request failed: " + failed.toString(), failed);
                this.logger.debug("Updated SecurityContextHolder to contain null Authentication");
                this.logger.debug("Delegating to authentication failure handler " +
                        this.failureHandler);
            }
            this.rememberMeServices.loginFail(request, response);
            this.failureHandler.onAuthenticationFailure(request, response, failed);
        }
    }

    可见AbstractAuthenticationProcessingFilter这个过滤器对于认证成功与否,做了两个分支,成功执行
    successfulAuthentication,失败执行unsuccessfulAuthentication。
    在successfulAuthentication内部,将认证信息存储到了SecurityContext中。并调用了loginSuccess方法,这就是
    常见的“记住我”功能!

  • 相关阅读:
    内存映射
    docstring show under decorator
    eventlet dbpool for postgresql &mysql
    za python
    Install MySQL 5.0 Max on FC3
    vi
    ff chrome tips
    20101004网站部署更新
    sqlalchemy type &elixir type
    20100930网站部署更新日志
  • 原文地址:https://www.cnblogs.com/dalianpai/p/12369277.html
Copyright © 2020-2023  润新知