收藏私塾在线
 

欢迎您来到私塾在线网!   

请登录! 

免费注册 


zhang的笔记
状态: 离线
人气:5106300
访问用户量:4228
笔记经验:
总积分: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源码分析
浏览(16603)|评论(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/

精品视频课程推荐

Java数据结构和算法精讲版
本课程专注于数据结构和算法的内容,使用Java来进行代码示例,不空洞的讲解概念和理论,重点放在代码的实现和示例上。 从零开始、全面系统、成体系的讲解数据结构和基本算法,循序渐进的讲述构建软件系统所常见的数据结构和算法。

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

研磨设计模式——跟着cc学设计系列视频教程
本视频课程是北京Java私塾原创精品书籍《研磨设计模式》一书的配套学习视频,由《研磨设计模式》的第一作者CC录制 课程目标:全面、系统的掌握GoF设计模式的知识,达到可以在实际项目开发中运用的能力 技术要点:如何实现可配置、如何实现缓存以及缓存的管理、如何实现用缓存来控制多实例的创建、如何实现参数化工厂、 如何实现可扩展工厂、如何实现原型管理器、如何实现Java的静态代理和动态代理、如何实现多线程处理队列请求、 如何实现命令的参数化配置、可撤销的操作、宏命令、队列请求和日志请求、如何实现翻页迭代、如何检测环状结构、 如何实现通用的增删改查、如何模拟工作流来处理流程、如何实现简单又通用的XML读取、如何实现模拟AOP的功能......

软件系统功能设计实战训练视频教程
本课程是《软件系统功能设计实战训练》网络班的全套学习视频,通过6个具体的设计案例,综合应用几乎全部的GoF设计模式,以及多种设计理念和方法。 每个设计案例都各有侧重点,训练的难度会逐步加大,以切实帮助各位学员快速的掌握软件系统设计的方法,提升自己的软件系统设计能力。

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

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

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

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

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