用户登录:Spring Security3源码分析-UsernamePasswordAuthenticationFilter分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

原文链接:http://dead-knight.iteye.com/blog/1513149 好好

参考链接2:http://blog.csdn.net/yecong111/article/details/17015199

如果用户没有自定义登录的url使用默认的url,路径是url:j_spring_security_check

过滤器UsernamePasswordAuthenticationFilter过滤到这个url。(实际上这个Filter类的doFilter是父类AbstractAuthenticationProcessingFilter的)。

AbstractAuthenticationProcessingFilter调用UsernamePasswordAuthenticationFilter中的attemptAuthentication方法,将表单请求的信息(用户、密码等信息)赋值给UsernamePasswordAuthenticationToken(authRequest),然后调用AbstractAuthenticationProcessingFilter中的AuthenticationManager类中的(默认是ProviderManager类)方法——getAuthenticationManager().authenticate(authRequest)对用户密码的正确性进行验证,认证失败就抛出异常,成功就返回Authentication对象。

UsernamePasswordAuthenticationFilter代码

        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
            //只处理post提交的请求
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
            }
            //获取用户名、密码数据
            String username = obtainUsername(request);
            String password = obtainPassword(request);

            if (username == null) {
                username = "";
            }

            if (password == null) {
                password = "";
            }

            username = username.trim();
            //构造未认证的UsernamePasswordAuthenticationToken
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

            // Place the last username attempted into HttpSession for views
            HttpSession session = request.getSession(false);
            //如果session不为空,添加username到session中
            if (session != null || getAllowSessionCreation()) {
                request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(username));
            }

            // Allow subclasses to set the "details" property
            //设置details,这里就是设置org.springframework.security.web.
            //authentication.WebAuthenticationDetails实例到details中
            setDetails(request, authRequest);
            //通过AuthenticationManager:ProviderManager完成认证任务
            return this.getAuthenticationManager().authenticate(authRequest);
        }

this.getAuthenticationManager().authenticate(authRequest)是指ProviderManager类中authenticate方法,ProviderManager类中authenticate方法里校验用户名密码的核心是providers。

ProviderManager代码

        public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {
            Class<? extends Authentication> toTest = authentication.getClass();
            AuthenticationException lastException = null;
            Authentication result = null;
            //循环ProviderManager中的providers,由具体的provider执行认证操作
            for (AuthenticationProvider provider : getProviders()) {
                System.out.println("AuthenticationProvider: " + provider.getClass().getName());
                if (!provider.supports(toTest)) {
                    continue;
                }

                logger.debug("Authentication attempt using " + provider.getClass().getName());

                try {
                    result = provider.authenticate(authentication);

                    if (result != null) {
                        //复制details
                        copyDetails(authentication, result);
                        break;
                    }
                } catch (AccountStatusException e) {
                    // SEC-546: Avoid polling additional providers if auth failure is due to invalid account status
                    eventPublisher.publishAuthenticationFailure(e, authentication);
                    throw e;
                } catch (AuthenticationException e) {
                    lastException = e;
                }
            }

            if (result == null && parent != null) {
                // Allow the parent to try.
                try {
                    result = parent.authenticate(authentication);
                } catch (ProviderNotFoundException e) {
                    // ignore as we will throw below if no other exception occurred prior to calling parent and the parent
                    // may throw ProviderNotFound even though a provider in the child already handled the request
                } catch (AuthenticationException e) {
                    lastException = e;
                }
            }

            if (result != null) {
                eventPublisher.publishAuthenticationSuccess(result);
                return result;
            }

            // Parent was null, or didn't authenticate (or throw an exception).

            if (lastException == null) {
                lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",
                            new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));
            }
            //由注入进来的org.springframework.security.authentication.DefaultAuthenticationEventPublisher完成事件发布任务
            eventPublisher.publishAuthenticationFailure(lastException, authentication);

            throw lastException;
        }

ProviderManager仅仅是管理provider的,具体的authenticate认证任务由各自provider来完成。 providers中的provider分别为:
org.springframework.security.authentication.dao.DaoAuthenticationProvider
org.springframework.security.authentication.AnonymousAuthenticationProvider
其他的provider根据特殊情况,再添加到providers中的,如remember me功能的provider
org.springframework.security.authentication.RememberMeAuthenticationProvider 。

AuthenticationProvider接口里有authenticate方法,实现类有多个:其中有AbstractUserDetailsAuthenticationProvider,AbstractUserDetailsAuthenticationProvider的子类DaoAuthenticationProvider(上文提到的providers中的provider) 中有retrieveUser方法,retrieveUser方法中有最重要的一句代码:

loadedUser = this.getUserDetailsService().loadUserByUsername(username);

这行代码中调用了UserDetailsService,我们通过实现UserDetailsService重写loadUserByUsername方法,就可以用查询数据库的方式从数据库表中获取用户名密码,而不是在配置文件中写死用户名密码。


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » 用户登录:Spring Security3源码分析-UsernamePasswordAuthenticationFilter分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏