UserNamePasswrodAuthenticationFilter验证过程

来源:互联网 发布:声音训练教程软件 编辑:程序博客网 时间:2024/04/27 22:08
体系结构
AuthenticationManager的体系结构
AuthenticationProvider
其实,在UserNamePasswrodAuthenticationFilter中,验证是通过AuthenticationManager,但AuthenticationManager就相当于一个AuthenticationProvider容器,会把验证转让给集合中的AuthenticationProvider去验证

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

登陆界面

页面源码

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


接下来看一下他是怎么处理的



进入attempAuthentication()中


再看一下getAuthenticationManager()
可见authenticationManager默认实现是ProviderManager


以下对ProviderManager的authenticate源码进行仔细解读了
//ProviderManager.authenticatepublic 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;    }



看上面代码中的第一个for循环

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


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


parent的验证过程也是一样的,只不过这次的provider是DaoAuthenticationProvider了


再看一下provider.authenticate()实现


看一下retriveUser()中的实现

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


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


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


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

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



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

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


于是,登录成功了

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





0 0
原创粉丝点击