- 浏览: 1193984 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
insistboy:
写的太棒了,受不了
WebLogic11g-创建域(Domain) -
goldyeah:
厉害了 困扰我大半个月的问题解决了 谢谢博主
WebLogic11g-单双向SSL配置(以Springside3为例) -
zy315351965:
404伤不起
开源流程引擎Snaker -
nannan408:
双向的时候谷歌提示“不接受您的登录证书,或者您可能没有提供登录 ...
WebLogic11g-单双向SSL配置(以Springside3为例) -
一颗赛艇:
不成功啊。启动有问题 <Security> < ...
WebLogic11g-单双向SSL配置(以Springside3为例)
UsernamePasswordAuthenticationFilter过滤器对应的类路径为
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
实际上这个Filter类的doFilter是父类AbstractAuthenticationProcessingFilter的
子类UsernamePasswordAuthenticationFilter的认证方法attemptAuthentication
这里的authenticationManager变量也是通过解析form-login标签,构造bean时注入的,具体解析类为:org.springframework.security.config.http.AuthenticationConfigBuilder
代码片段为:
继续看ProviderManager代码。实际上authenticate方法由ProviderManager的父类定义,并且authenticate方法内调用子类的doAuthentication方法,记得这是设计模式中的模板模式
ProviderManager类中的providers由哪些provider呢?如果看完authentication-manager标签解析的讲解,应该知道注入到providers中的provider分别为:
org.springframework.security.authentication.dao.DaoAuthenticationProvider
org.springframework.security.authentication.AnonymousAuthenticationProvider
其他的provider根据特殊情况,再添加到providers中的,如remember me功能的provider
org.springframework.security.authentication.RememberMeAuthenticationProvider
可以看出来,ProviderManager仅仅是管理provider的,具体的authenticate认证任务由各自provider来完成。
现在来看DaoAuthenticationProvider的认证处理,实际上authenticate由父类AbstractUserDetailsAuthenticationProvider完成。代码如下
继续看DaoAuthenticationProvider的retrieveUser方法
实际上,只要实现UserDetailsService接口的loadUserByUsername方法,就完成了登录认证的工作
很多教程上说配置JdbcUserDetailsManager这个UserDetailsService,实际上该类的父类
JdbcDaoImpl方法loadUserByUsername代码如下:
其他的provider,如
RememberMeAuthenticationProvider、AnonymousAuthenticationProvider的认证处理都很简单,首先判断是否支持Authentication,不支持直接返回null,支持也不处理直接返回该Authentication
这里需要强调一下,DaoAuthenticationProvider只支持UsernamePasswordAuthenticationToken这个Authentication。如果对其他的Authentication,DaoAuthenticationProvider是不做处理的
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
实际上这个Filter类的doFilter是父类AbstractAuthenticationProcessingFilter的
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; //判断form-login标签是否包含login-processing-url属性 //如果没有采用默认的url:j_spring_security_check //如果拦截的url不需要认证,直接跳过 if (!requiresAuthentication(request, response)) { chain.doFilter(request, response); return; } if (logger.isDebugEnabled()) { logger.debug("Request is to process authentication"); } Authentication authResult; try { //由子类完成认证 authResult = attemptAuthentication(request, response); if (authResult == null) { // return immediately as subclass has indicated that it hasn't completed authentication return; } //session策略处理认证信息 //sessionStrategy是通过session-management标签中定义的 //session管理策略构造的SessionAuthenticationStrategy //具体的session管理比较复杂,部分后面单个篇幅讲解 sessionStrategy.onAuthentication(authResult, request, response); } catch (AuthenticationException failed) { // Authentication failed //认证失败处理 unsuccessfulAuthentication(request, response, failed); return; } // Authentication success if (continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } //认证成功处理 //1.向SecurityContext中设置Authentication认证信息 //2.如果有remember me服务,则查找请求参数中是否包含_spring_security_remember_me,如果该参数值为true、yes、on、1则执行remember me功能:添加cookie、入库。为下次请求时自动登录做准备 //3.发布认证成功事件 //4.执行跳转 successfulAuthentication(request, response, authResult); }
子类UsernamePasswordAuthenticationFilter的认证方法attemptAuthentication
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); }
这里的authenticationManager变量也是通过解析form-login标签,构造bean时注入的,具体解析类为:org.springframework.security.config.http.AuthenticationConfigBuilder
代码片段为:
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager) { Element formLoginElt = DomUtils.getChildElementByTagName(httpElt, Elements.FORM_LOGIN); if (formLoginElt != null || autoConfig) { FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_security_check", AUTHENTICATION_PROCESSING_FILTER_CLASS, requestCache, sessionStrategy); parser.parse(formLoginElt, pc); formFilter = parser.getFilterBean(); formEntryPoint = parser.getEntryPointBean(); } if (formFilter != null) { formFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation)); //设置authenticationManager的bean依赖 formFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager); // Id is required by login page filter formFilterId = pc.getReaderContext().generateBeanName(formFilter); pc.registerBeanComponent(new BeanComponentDefinition(formFilter, formFilterId)); injectRememberMeServicesRef(formFilter, rememberMeServicesId); } }
继续看ProviderManager代码。实际上authenticate方法由ProviderManager的父类定义,并且authenticate方法内调用子类的doAuthentication方法,记得这是设计模式中的模板模式
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类中的providers由哪些provider呢?如果看完authentication-manager标签解析的讲解,应该知道注入到providers中的provider分别为:
org.springframework.security.authentication.dao.DaoAuthenticationProvider
org.springframework.security.authentication.AnonymousAuthenticationProvider
其他的provider根据特殊情况,再添加到providers中的,如remember me功能的provider
org.springframework.security.authentication.RememberMeAuthenticationProvider
可以看出来,ProviderManager仅仅是管理provider的,具体的authenticate认证任务由各自provider来完成。
现在来看DaoAuthenticationProvider的认证处理,实际上authenticate由父类AbstractUserDetailsAuthenticationProvider完成。代码如下
public Authentication authenticate(Authentication authentication) throws AuthenticationException { ………… //获取登录的用户名 String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName(); boolean cacheWasUsed = true; //如果配置了缓存,从缓存中获取UserDetails实例 UserDetails user = this.userCache.getUserFromCache(username); if (user == null) { cacheWasUsed = false; try { //如果UserDetails为空,则由具体子类DaoAuthenticationProvider //根据用户名、authentication获取UserDetails user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); } catch (UsernameNotFoundException notFound) { if (hideUserNotFoundExceptions) { throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } else { throw notFound; } } Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); } try { //一些认证检查(账号是否可用、是否过期、是否被锁定) preAuthenticationChecks.check(user); //额外的密码检查(salt、passwordEncoder) additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } catch (AuthenticationException exception) { if (cacheWasUsed) { // There was a problem, so try again after checking // we're using latest data (i.e. not from the cache) cacheWasUsed = false; user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } else { throw exception; } } //检查账号是否过期 postAuthenticationChecks.check(user); //添加UserDetails到缓存中 if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (forcePrincipalAsString) { principalToReturn = user.getUsername(); } //返回成功认证后的Authentication return createSuccessAuthentication(principalToReturn, authentication, user); }
继续看DaoAuthenticationProvider的retrieveUser方法
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { UserDetails loadedUser; try { //最关键的部分登场了 //UserDetailService就是authentication-provider标签中定义的 //属性user-service-ref loadedUser = this.getUserDetailsService().loadUserByUsername(username); } catch (DataAccessException repositoryProblem) { throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem); } if (loadedUser == null) { throw new AuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; }
实际上,只要实现UserDetailsService接口的loadUserByUsername方法,就完成了登录认证的工作
<authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="userDetailsManager"/> </authentication-manager>
很多教程上说配置JdbcUserDetailsManager这个UserDetailsService,实际上该类的父类
JdbcDaoImpl方法loadUserByUsername代码如下:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { //根据username从数据库中查询User数据 List<UserDetails> users = loadUsersByUsername(username); if (users.size() == 0) { throw new UsernameNotFoundException( messages.getMessage("JdbcDaoImpl.notFound", new Object[]{username}, "Username {0} not found"), username); } UserDetails user = users.get(0); // contains no GrantedAuthority[] Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>(); //添加授权信息 if (enableAuthorities) { dbAuthsSet.addAll(loadUserAuthorities(user.getUsername())); } //是否使用了Group if (enableGroups) { dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername())); } List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet); addCustomAuthorities(user.getUsername(), dbAuths); if (dbAuths.size() == 0) { throw new UsernameNotFoundException( messages.getMessage("JdbcDaoImpl.noAuthority", new Object[] {username}, "User {0} has no GrantedAuthority"), username); } return createUserDetails(username, user, dbAuths); } //usersByUsernameQuery查询语句可配置 //直接从数据库中查询该username对应的数据,并构造User对象 protected List<UserDetails> loadUsersByUsername(String username) { return getJdbcTemplate().query(usersByUsernameQuery, new String[] {username}, new RowMapper<UserDetails>() { public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException { String username = rs.getString(1); String password = rs.getString(2); boolean enabled = rs.getBoolean(3); return new User(username, password, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES); } }); } …… protected UserDetails createUserDetails(String username, UserDetails userFromUserQuery, List<GrantedAuthority> combinedAuthorities) { String returnUsername = userFromUserQuery.getUsername(); if (!usernameBasedPrimaryKey) { returnUsername = username; } //根据用户名、密码、enabled、授权列表构造UserDetails实例User return new User(returnUsername, userFromUserQuery.getPassword(), userFromUserQuery.isEnabled(), true, true, true, combinedAuthorities); }
其他的provider,如
RememberMeAuthenticationProvider、AnonymousAuthenticationProvider的认证处理都很简单,首先判断是否支持Authentication,不支持直接返回null,支持也不处理直接返回该Authentication
这里需要强调一下,DaoAuthenticationProvider只支持UsernamePasswordAuthenticationToken这个Authentication。如果对其他的Authentication,DaoAuthenticationProvider是不做处理的
发表评论
-
Spring Security3源码分析-电子书下载
2012-07-30 14:34 8552提供电子书下载链接。 -
Spring Security3源码分析-CAS支持
2012-05-13 21:03 25784Spring Security3对CAS的支持主要在这个spr ... -
Spring Security3源码分析-SSL支持
2012-05-10 12:48 11081Sping Security3对于SSL的支持仅仅表现在对需要 ... -
Spring Security3源码分析-认证授权分析
2012-05-09 21:59 6370前面分析了FilterChainProxy执行过程,也对常用的 ... -
Spring Security3源码分析-Filter链排序分析
2012-05-09 14:39 15311通过前面Spring Security提供的各种Filter的 ... -
Spring Security3源码分析-RequestCacheAwareFilter分析
2012-05-09 12:55 4888RequestCacheAwareFilter过滤器对应的类路 ... -
Spring Security3源码分析-ExceptionTranslationFilter分析
2012-05-09 10:03 7808ExceptionTranslationFilter过滤器对应 ... -
Spring Security3源码分析-SessionManagementFilter分析-下
2012-05-08 21:03 6339很多spring security3资料在 ... -
Spring Security3源码分析-SessionManagementFilter分析-上
2012-05-08 17:26 10926SessionManagementFilter过滤 ... -
Spring Security3源码分析-AnonymousAuthenticationFilter分析
2012-05-08 10:32 5199AnonymousAuthenticationFilter ... -
Spring Security3源码分析-BasicAuthenticationFilter分析
2012-05-08 09:24 9609BasicAuthenticationFilter过滤器对应的 ... -
Spring Security3源码分析-FilterSecurityInterceptor分析
2012-05-07 17:31 15293FilterSecurityInterceptor过滤器对应的 ... -
Spring Security3源码分析-SecurityContextHolderAwareRequestFilter分析
2012-05-07 10:34 6822SecurityContextHolderAwareReque ... -
Spring Security3源码分析-RememberMeAuthenticationFilter分析
2012-05-06 22:33 5954RememberMeAuthenticationFilter过 ... -
Spring Security3源码分析-LogoutFilter分析
2012-05-06 10:18 10306LogoutFilter过滤器对应的类路径为 org.spri ... -
Spring Security3源码分析-SecurityContextPersistenceFilter分析
2012-05-06 08:22 8732通过观察Filter的名字,就能大概猜出来这个过滤器的作用,是 ... -
Spring Security3源码分析-FilterChainProxy执行过程分析
2012-05-06 07:48 4246通过FilterChainProxy的初始化、自定义标签的分析 ... -
Spring Security3源码分析-authentication-manager标签解析
2012-05-05 16:13 21686讲解完http标签的解析过程,authentication-m ... -
Spring Security3源码分析-http标签解析
2012-05-05 15:29 8450在FilterChainProxy初始化的 ... -
Spring Security3源码分析-FilterChainProxy初始化
2012-05-04 16:57 20056很久没有更新博客了,最近对Spring Security做了比 ...
相关推荐
Spring Security-3.0.1 中文官方文档(翻译版) 这次发布的Spring Security-3.0.1 是一个bug fix 版,主要是对3.0 中存在的一些问题进 行修 正。文档中没有添加新功能的介绍,但是将之前拼写错误的一些类名进行...
[输入图片说明](https://gitee.com/uploads/images/2018/0211/154308_9576ce90_130820.png "jwt-3.png") 4.用登录成功后拿到的token再次请求/users/userList接口 4.1将请求中的XXXXXX替换成拿到的token ...
1.1. Spring Security是什么? 1.2. 历史 1.3. 发行版本号 1.4. 获得Spring Security 1.4.1. 项目模块 1.4.1.1. Core - spring-security-core.jar 1.4.1.2. Web - spring-security-web.jar 1.4.1.3. ...
3. Spring Security 4.2的新特性 27 3.1 Web改进 27 3.2配置改进 28 3.3杂项 28 4.样品和指南(从这里开始) 28 5. Java配置 29 5.1 Hello Web安全Java配置 29 5.1.1 AbstractSecurityWebApplicationInitializer 31 ...
1.1. Spring Security是什么? 1.2. 历史 1.3. 发行版本号 1.4. 获得Spring Security 1.4.1. 项目模块 1.4.1.1. Core - spring-security-core.jar 1.4.1.2. Web - spring-security-web.jar 1.4.1.3. Config -...
在集成 Spring Security 安全框架的时候我们最先处理的可能就是根据我们项目的实际需要来定制注册登录了,尤其是 Http 登录认证。根据以前的相关文章介绍, Http 登录...今天我们就简单分析它的源码和工作流程。
Security 有两种认证方式: httpbasic formLogin 默认的,如上边那种方式 同样,Security 也提供两种过滤器类: UsernamePasswordAuthenticationFilter 表示表单登陆过滤器 BasicAuthenticationFilter 表示 ...
3 = {CsrfFilter @ 7758} 4 = {LogoutFilter @ 7757} 5 = {UsernamePasswordAuthenticationFilter @ 7755} 6 = {RequestCacheAwareFilter @ 9937} 7 = {SecurityContextHolderAwareRequestFilter @ 9938} 8 = {...
传统的方法是在认证通过后,创建...核心功能是在验证用户名密码正确后,生成一个token,并将token返回给客户端:该类继承自UsernamePasswordAuthenticationFilter,重写了其中的2个方法:attemptAuthentication :接