首页 > 其他 > 详细

UserNamePasswrodAuthenticationFilter验证过程

时间:2014-03-09 18:29:11      阅读:605      评论:0      收藏:0      [点我收藏+]
体系结构
AuthenticationManager的体系结构
bubuko.com,布布扣
bubuko.com,布布扣
AuthenticationProvider
bubuko.com,布布扣
bubuko.com,布布扣
其实,在UserNamePasswrodAuthenticationFilter中,验证是通过AuthenticationManager,但AuthenticationManager就相当于一个AuthenticationProvider容器,会把验证转让给集合中的AuthenticationProvider去验证

applicationContext-security.xml中的配置
bubuko.com,布布扣
请求一个index.jsp页面,由于页面不足,他会自动把请求承递到spring_security_login,也就是由DefaultLoginPageGeneratingFilter自动生成的一个登录页面

登陆界面
bubuko.com,布布扣

页面源码
bubuko.com,布布扣
第一次登录页面时候,服务器会产生一个Session,并把cookieid返回来给浏览器,所以可以看到请求地址行的请求参数中带有jsesessionid,其实就是一个cookie值了
同时,以spring_security_check结尾(不算上jessessionid参数)所以会激发filter UserNamePasswrodAuthenticationFilter了


接下来看一下他是怎么处理的
bubuko.com,布布扣
bubuko.com,布布扣


进入attempAuthentication()中
bubuko.com,布布扣


再看一下getAuthenticationManager()
bubuko.com,布布扣
可见authenticationManager默认实现是ProviderManager


以下对ProviderManager的authenticate源码进行仔细解读了
//ProviderManager.authenticate
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        Authentication result = null;
        boolean debug = logger.isDebugEnabled();
        for (AuthenticationProvider provider : getProviders()) {
            if (!provider.supports(toTest)) {
                continue;
            }
            if (debug) {
                logger.debug("Authentication attempt using " + provider.getClass().getName());
            }
            try {
                result = provider.authenticate(authentication);
                if (result != null) {
                    copyDetails(authentication, result);
                    break;
                }
            } catch (AccountStatusException e) {
                prepareException(e, authentication);
                // SEC-546: Avoid polling additional providers if auth failure is due to invalid account status
                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) {
            if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
                // Authentication is complete. Remove credentials and other secret data from authentication
                ((CredentialsContainer)result).eraseCredentials();
            }
            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}"));
        }
        prepareException(lastException, authentication);
        throw lastException;
    }




bubuko.com,布布扣
配置文件中没有配置其他的AuthenticationProvider,所以采用默认实现AnonymousAuthenticationManager去验证
bubuko.com,布布扣

bubuko.com,布布扣
这里可见AuthenticationManager其实里面还分装这一个parent,他也是一个ProviderManager(AuthenticationManager),而他的默认的providers集合里面有一个DaoAuthenticationProvider


parent的验证过程也是一样的,只不过这次的provider是DaoAuthenticationProvider了
bubuko.com,布布扣
bubuko.com,布布扣

再看一下provider.authenticate()实现
bubuko.com,布布扣
bubuko.com,布布扣
bubuko.com,布布扣bubuko.com,布布扣

看一下retriveUser()中的实现
bubuko.com,布布扣
这里是根据我们登录界面提交的用户名去检索一个UserDetails,其中包括用户的密码,权限还有一下相信信息,我们只要在Hibernate中加入一个UserDetailsService就可以实现,用户名和密码等存储在数据库了,当然,我们这里只是简单的将用户名guest和密码guest配置在配置文件中而已了


再看一下additionAuthenticationChecks()中的实现吧

bubuko.com,布布扣

这里简单的检查一下,我们的密码有没有加入艳值salt(salt可以在密码中对密码加密的时候一起加上去加密,增加破解难度),明显,我们这里没有,然后他会把我们之前在登录界面输入用户名密码的一个封装Authentication和数据库或者配置文件中获取的UserDetails的密码进行匹配


于是,验证成功了,回到AbstractAuthenticationFilter中看successfulAuthentication(request, response, chain, authResult);的实现,他会接着调用successfulAuthentication()

bubuko.com,布布扣红色部分,将验证成功的实体加入SecurityContextHolder,如果配置了rememberMeService,他还会将请求成功的实体加入rememberme中的缓存,以便下次可以实现remember me功能了



验证成功了,filter链不再继续进行下去了,于是一路返回

我们回去看SecurityContextPersistentFilter中借来下的处理吧
bubuko.com,布布扣
可见,验证登录过的用户下次就不用在登录了,因为springsecurity已经把上次登录成功的securityContext加入了SecurityContextRepository了


于是,登录成功了
bubuko.com,布布扣
bubuko.com,布布扣
总结
1、UserNamePasswrodAuthenticationFilter的处理过程就是先从登录界面中得到的Authentication(用户名和密码的封装实体)中的用户名去寻找配置文件或者数据库中的用户信息,并把该信息进行分装成一个UserDetails
2、将Authentication和UserDetails进行密码的匹配,可能还会涉及到盐值salt
3、若请求成功,则进行一些列设定,包括securityContext、rememberme、还有缓存该登陆成功的用户信息,用session进行标识,避免他登录过期,替换新的session(这会根据你在sessionManngerfilter中的配置,是新建还是迁移)

UserNamePasswrodAuthenticationFilter验证过程,布布扣,bubuko.com

UserNamePasswrodAuthenticationFilter验证过程

原文:http://blog.csdn.net/chenxuegui1234/article/details/20836539

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!