收藏私塾在线
 

欢迎您来到私塾在线网!   

请登录! 

免费注册 


zhang的笔记
状态: 离线
人气:5023375
访问用户量:4221
笔记经验:
总积分:261656
级别:VIP5
搜索本笔记
ta的交流分类
ta的交流主题贴(544)
ta的所有交流贴(1049)
ta的全部笔记
全部笔记(255)
未分类笔记(1)
Java Web(9)
并发实践(1)
课程问题(0)
Java(22)
架构(1)
缓存(5)
JavaEE(0)
JVM(12)
跟我学spring3(68)
Spring Sec……(43)
Spring 3.x……(25)
Spring Sec……(20)
跟开涛学Spring……(17)
深入剖析Spring……(18)
性能调优(10)
前端(2)
Tomcat源码解读(1)
spring sec……(0)
存档
2014-01(7)
2013-12(10)
2012-10(4)
2012-09(2)
2012-08(31)
2012-07(10)
2012-06(5)
2012-05(41)
2012-04(3)
2012-03(41)
2012-02(54)
2011-11(17)
2011-10(30)

2012-05-10 15:14:28
UsernamePasswordAuthenticationFilter分析——Spring Security3源码分析
浏览(16159)|评论(0)   交流分类:Java|笔记分类: Spring Sec……

UsernamePasswordAuthenticationFilter过滤器对应的类路径为 
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter 
实际上这个Filter类的doFilter是父类AbstractAuthenticationProcessingFilter的 

Java代码    收藏代码
  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)  
  2.         throws IOException, ServletException {  
  3.   
  4.     HttpServletRequest request = (HttpServletRequest) req;  
  5.     HttpServletResponse response = (HttpServletResponse) res;  
  6.     //判断form-login标签是否包含login-processing-url属性  
  7.       //如果没有采用默认的url:j_spring_security_check  
  8.     //如果拦截的url不需要认证,直接跳过  
  9.     if (!requiresAuthentication(request, response)) {  
  10.         chain.doFilter(request, response);  
  11.   
  12.         return;  
  13.     }  
  14.   
  15.     if (logger.isDebugEnabled()) {  
  16.         logger.debug("Request is to process authentication");  
  17.     }  
  18.   
  19.     Authentication authResult;  
  20.   
  21.     try {  
  22.         //由子类完成认证  
  23.         authResult = attemptAuthentication(request, response);  
  24.         if (authResult == null) {  
  25.             // return immediately as subclass has indicated that it hasn't completed authentication  
  26.             return;  
  27.         }  
  28.         //session策略处理认证信息  
  29.           //sessionStrategy是通过session-management标签中定义的  
  30.           //session管理策略构造的SessionAuthenticationStrategy  
  31.         //具体的session管理比较复杂,部分后面单个篇幅讲解  
  32.         sessionStrategy.onAuthentication(authResult, request, response);  
  33.     }  
  34.     catch (AuthenticationException failed) {  
  35.         // Authentication failed  
  36.         //认证失败处理  
  37.         unsuccessfulAuthentication(request, response, failed);  
  38.   
  39.         return;  
  40.     }  
  41.   
  42.     // Authentication success  
  43.     if (continueChainBeforeSuccessfulAuthentication) {  
  44.         chain.doFilter(request, response);  
  45.     }  
  46.     //认证成功处理  
  47.      //1.向SecurityContext中设置Authentication认证信息  
  48.      //2.如果有remember me服务,则查找请求参数中是否包含_spring_security_remember_me,如果该参数值为true、yes、on、1则执行remember me功能:添加cookie、入库。为下次请求时自动登录做准备  
  49.      //3.发布认证成功事件  
  50.      //4.执行跳转  
  51.     successfulAuthentication(request, response, authResult);  
  52. }  


子类UsernamePasswordAuthenticationFilter的认证方法attemptAuthentication 

Java代码    收藏代码
  1. public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {  
  2.     //只处理post提交的请求  
  3.     if (postOnly && !request.getMethod().equals("POST")) {  
  4.         throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());  
  5.     }  
  6.     //获取用户名、密码数据  
  7.     String username = obtainUsername(request);  
  8.     String password = obtainPassword(request);  
  9.   
  10.     if (username == null) {  
  11.         username = "";  
  12.     }  
  13.   
  14.     if (password == null) {  
  15.         password = "";  
  16.     }  
  17.   
  18.     username = username.trim();  
  19.     //构造未认证的UsernamePasswordAuthenticationToken  
  20.     UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);  
  21.   
  22.     // Place the last username attempted into HttpSession for views  
  23.     HttpSession session = request.getSession(false);  
  24.     //如果session不为空,添加username到session中  
  25.     if (session != null || getAllowSessionCreation()) {  
  26.         request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(username));  
  27.     }  
  28.   
  29.     // Allow subclasses to set the "details" property  
  30.     //设置details,这里就是设置org.springframework.security.web.  
  31.     //authentication.WebAuthenticationDetails实例到details中  
  32.     setDetails(request, authRequest);  
  33.     //通过AuthenticationManager:ProviderManager完成认证任务  
  34.     return this.getAuthenticationManager().authenticate(authRequest);  
  35. }  


这里的authenticationManager变量也是通过解析form-login标签,构造bean时注入的,具体解析类为:org.springframework.security.config.http.AuthenticationConfigBuilder 
代码片段为: 

Java代码    收藏代码
  1. void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {  
  2.   
  3.     Element formLoginElt = DomUtils.getChildElementByTagName(httpElt, Elements.FORM_LOGIN);  
  4.   
  5.     if (formLoginElt != null || autoConfig) {  
  6.         FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_security_check",  
  7.                 AUTHENTICATION_PROCESSING_FILTER_CLASS, requestCache, sessionStrategy);  
  8.   
  9.         parser.parse(formLoginElt, pc);  
  10.         formFilter = parser.getFilterBean();  
  11.         formEntryPoint = parser.getEntryPointBean();  
  12.     }  
  13.   
  14.     if (formFilter != null) {  
  15.         formFilter.getPropertyValues().addPropertyValue("allowSessionCreation"new Boolean(allowSessionCreation));  
  16.         //设置authenticationManager的bean依赖  
  17.         formFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);  
  18.   
  19.   
  20.         // Id is required by login page filter  
  21.         formFilterId = pc.getReaderContext().generateBeanName(formFilter);  
  22.         pc.registerBeanComponent(new BeanComponentDefinition(formFilter, formFilterId));  
  23.         injectRememberMeServicesRef(formFilter, rememberMeServicesId);  
  24.     }  
  25. }  



继续看ProviderManager代码。实际上authenticate方法由ProviderManager的父类定义,并且authenticate方法内调用子类的doAuthentication方法,记得这是设计模式中的模板模式 

Java代码    收藏代码
  1. public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {  
  2.     Class<? extends Authentication> toTest = authentication.getClass();  
  3.     AuthenticationException lastException = null;  
  4.     Authentication result = null;  
  5.     //循环ProviderManager中的providers,由具体的provider执行认证操作  
  6.     for (AuthenticationProvider provider : getProviders()) {  
  7.         System.out.println("AuthenticationProvider: " + provider.getClass().getName());  
  8.         if (!provider.supports(toTest)) {  
  9.             continue;  
  10.         }  
  11.   
  12.         logger.debug("Authentication attempt using " + provider.getClass().getName());  
  13.   
  14.         try {  
  15.             result = provider.authenticate(authentication);  
  16.   
  17.             if (result != null) {  
  18.                 //复制details  
  19.                 copyDetails(authentication, result);  
  20.                 break;  
  21.             }  
  22.         } catch (AccountStatusException e) {  
  23.             // SEC-546: Avoid polling additional providers if auth failure is due to invalid account status  
  24.             eventPublisher.publishAuthenticationFailure(e, authentication);  
  25.             throw e;  
  26.         } catch (AuthenticationException e) {  
  27.             lastException = e;  
  28.         }  
  29.     }  
  30.   
  31.     if (result == null && parent != null) {  
  32.         // Allow the parent to try.  
  33.         try {  
  34.             result = parent.authenticate(authentication);  
  35.         } catch (ProviderNotFoundException e) {  
  36.             // ignore as we will throw below if no other exception occurred prior to calling parent and the parent  
  37.             // may throw ProviderNotFound even though a provider in the child already handled the request  
  38.         } catch (AuthenticationException e) {  
  39.             lastException = e;  
  40.         }  
  41.     }  
  42.   
  43.     if (result != null) {  
  44.         eventPublisher.publishAuthenticationSuccess(result);  
  45.         return result;  
  46.     }  
  47.   
  48.     // Parent was null, or didn't authenticate (or throw an exception).  
  49.   
  50.     if (lastException == null) {  
  51.         lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",  
  52.                     new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));  
  53.     }  
  54.     //由注入进来的org.springframework.security.authentication.DefaultAuthenticationEventPublisher完成事件发布任务  
  55.     eventPublisher.publishAuthenticationFailure(lastException, authentication);  
  56.   
  57.     throw lastException;  
  58. }  



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完成。代码如下 

Java代码    收藏代码
  1. public Authentication authenticate(Authentication authentication) throws AuthenticationException {  
  2.     …………  
  3.      //获取登录的用户名  
  4.     String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();  
  5.   
  6.     boolean cacheWasUsed = true;  
  7.     //如果配置了缓存,从缓存中获取UserDetails实例  
  8.     UserDetails user = this.userCache.getUserFromCache(username);  
  9.   
  10.     if (user == null) {  
  11.         cacheWasUsed = false;  
  12.   
  13.         try {  
  14.             //如果UserDetails为空,则由具体子类DaoAuthenticationProvider  
  15.             //根据用户名、authentication获取UserDetails  
  16.             user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);  
  17.         } catch (UsernameNotFoundException notFound) {  
  18.             if (hideUserNotFoundExceptions) {  
  19.                 throw new BadCredentialsException(messages.getMessage(  
  20.                         "AbstractUserDetailsAuthenticationProvider.badCredentials""Bad credentials"));  
  21.             } else {  
  22.                 throw notFound;  
  23.             }  
  24.         }  
  25.   
  26.         Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");  
  27.     }  
  28.   
  29.     try {  
  30.         //一些认证检查(账号是否可用、是否过期、是否被锁定)  
  31.         preAuthenticationChecks.check(user);  
  32.         //额外的密码检查(salt、passwordEncoder)  
  33.         additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);  
  34.     } catch (AuthenticationException exception) {  
  35.         if (cacheWasUsed) {  
  36.             // There was a problem, so try again after checking  
  37.             // we're using latest data (i.e. not from the cache)  
  38.             cacheWasUsed = false;  
  39.             user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);  
  40.             preAuthenticationChecks.check(user);  
  41.             additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);  
  42.         } else {  
  43.             throw exception;  
  44.         }  
  45.     }  
  46.     //检查账号是否过期  
  47.     postAuthenticationChecks.check(user);  
  48.     //添加UserDetails到缓存中  
  49.     if (!cacheWasUsed) {  
  50.         this.userCache.putUserInCache(user);  
  51.     }  
  52.   
  53.     Object principalToReturn = user;  
  54.   
  55.     if (forcePrincipalAsString) {  
  56.         principalToReturn = user.getUsername();  
  57.     }  
  58.     //返回成功认证后的Authentication  
  59.     return createSuccessAuthentication(principalToReturn, authentication, user);  
  60. }  



继续看DaoAuthenticationProvider的retrieveUser方法 

Java代码    收藏代码
  1. protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)  
  2.         throws AuthenticationException {  
  3.     UserDetails loadedUser;  
  4.   
  5.     try {  
  6.         //最关键的部分登场了  
  7.           //UserDetailService就是authentication-provider标签中定义的  
  8.           //属性user-service-ref  
  9.         loadedUser = this.getUserDetailsService().loadUserByUsername(username);  
  10.     }  
  11.     catch (DataAccessException repositoryProblem) {  
  12.         throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);  
  13.     }  
  14.   
  15.     if (loadedUser == null) {  
  16.         throw new AuthenticationServiceException(  
  17.                 "UserDetailsService returned null, which is an interface contract violation");  
  18.     }  
  19.     return loadedUser;  
  20. }  


实际上,只要实现UserDetailsService接口的loadUserByUsername方法,就完成了登录认证的工作 

Xml代码    收藏代码
  1. <authentication-manager alias="authenticationManager">  
  2.     <authentication-provider user-service-ref="userDetailsManager"/>  
  3. </authentication-manager>  


很多教程上说配置JdbcUserDetailsManager这个UserDetailsService,实际上该类的父类 
JdbcDaoImpl方法loadUserByUsername代码如下: 

Java代码    收藏代码
  1.     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {  
  2.         //根据username从数据库中查询User数据  
  3.         List<UserDetails> users = loadUsersByUsername(username);  
  4.   
  5.         if (users.size() == 0) {  
  6.             throw new UsernameNotFoundException(  
  7.                     messages.getMessage("JdbcDaoImpl.notFound"new Object[]{username}, "Username {0} not found"), username);  
  8.         }  
  9.   
  10.         UserDetails user = users.get(0); // contains no GrantedAuthority[]  
  11.   
  12.         Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>();  
  13.         //添加授权信息  
  14.         if (enableAuthorities) {  
  15.             dbAuthsSet.addAll(loadUserAuthorities(user.getUsername()));  
  16.         }  
  17.         //是否使用了Group  
  18.         if (enableGroups) {  
  19.             dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername()));  
  20.         }  
  21.   
  22.         List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet);  
  23.   
  24.         addCustomAuthorities(user.getUsername(), dbAuths);  
  25.   
  26.         if (dbAuths.size() == 0) {  
  27.             throw new UsernameNotFoundException(  
  28.                     messages.getMessage("JdbcDaoImpl.noAuthority",  
  29.                             new Object[] {username}, "User {0} has no GrantedAuthority"), username);  
  30.         }  
  31.   
  32.         return createUserDetails(username, user, dbAuths);  
  33.     }  
  34.   
  35.     //usersByUsernameQuery查询语句可配置  
  36.      //直接从数据库中查询该username对应的数据,并构造User对象  
  37.     protected List<UserDetails> loadUsersByUsername(String username) {  
  38.         return getJdbcTemplate().query(usersByUsernameQuery, new String[] {username}, new RowMapper<UserDetails>() {  
  39.             public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException {  
  40.                 String username = rs.getString(1);  
  41.                 String password = rs.getString(2);  
  42.                 boolean enabled = rs.getBoolean(3);  
  43.                 return new User(username, password, enabled, truetruetrue, AuthorityUtils.NO_AUTHORITIES);  
  44.             }  
  45.   
  46.         });  
  47.     }  
  48.   
  49. ……  
  50.     protected UserDetails createUserDetails(String username, UserDetails userFromUserQuery,  
  51.             List<GrantedAuthority> combinedAuthorities) {  
  52.         String returnUsername = userFromUserQuery.getUsername();  
  53.   
  54.         if (!usernameBasedPrimaryKey) {  
  55.             returnUsername = username;  
  56.         }  
  57.         //根据用户名、密码、enabled、授权列表构造UserDetails实例User  
  58.         return new User(returnUsername, userFromUserQuery.getPassword(), userFromUserQuery.isEnabled(),  
  59.                 truetruetrue, combinedAuthorities);  
  60.     }  


其他的provider,如 
RememberMeAuthenticationProvider、AnonymousAuthenticationProvider的认证处理都很简单,首先判断是否支持Authentication,不支持直接返回null,支持也不处理直接返回该Authentication 

这里需要强调一下,DaoAuthenticationProvider只支持UsernamePasswordAuthenticationToken这个Authentication。如果对其他的Authentication,DaoAuthenticationProvider是不做处理的

 

 

http://sishuok.com/forum/blogPost/list/0/4301.html

 

转载自【http://dead-knight.iteye.com/blog/1513149

作者博客:http://dead-knight.iteye.com/

精品视频课程推荐

《设计模式综合项目实战》——跟着cc学设计系列精品视频教程

深入浅出学Spring Web MVC视频教程
系统、完整的学习Spring Web MVC开发的知识。包括:Spring Web MVC入门;理解DispatcherServlet;注解式控制器开发详解;数据类型转换;数据格式化;数据验证; 拦截器;对Ajax的支持;文件上传下载;表单标签等内容;最后以一个综合的CRUD带翻页的应用示例来综合所学的知识

Weblogic实战视频教程
WebLogic基础知识:WebLogic基本概念、正确安装WebLogic、建域、应用部署于JDBC选择、对WebLogic的监控和日志查看、集群的高可用性;课程目标:彻底掌握WebLogic的基本概念,在理解基本概念的基础上做到正确的安装WebLogic,根据不同的需求创建域,合理选择应用部署和JDBC配置。熟练掌握WebLogic的console监控,了解各种性能和运行指标,以及对监控结果的分析,运用集群的高可用性,对集群架设。

高级软件架构师实战培训阶段一
内容概述:本课程专注于构建:高可扩展性、高性能、大数据量、高并发、分布式的系统架构。 从零开始、全面系统、成体系的软件架构课程,循序渐进的讲述构建上述系统架构所需要的各种技术知识和技能。
技术要点: 1:构建基本的业务功能块,基于Maven+Git+Spring mvc+spring+mybatis+ehcache+mysql+X-gen代码生成
 2:高扩展性的分布式体系架构(基于Nginx+Varnish+Memcache+ActiveMQ)
 3:NoSQL的合理使用和架构优化(基于MongoDB)
 4:分布式文件存储和架构优化(基于MogileFS)

深入浅出学Spring Data JPA视频教程
系统、完整的学习Spring Data JPA开发的知识。包括:Spring Data JPA入门;JpaRepository基本功能 ;JpaRepository的查询;客户化扩展JpaRepository;Specifications查询。

浏览(16159)|评论(0)   交流分类:Java|笔记分类: Spring Sec……

评论(0)
请登录后评论 登录

关于我们 | 联系我们 | 用户协议 | 私塾在线服务协议 | 版权声明 | 隐私保护

版权所有 Copyright(C)2009-2012 私塾在线学习网