内容简介:用户登录基本流程处理如下:1 SecurityContextPersistenceFilter
springSecurity 拦截器链
用户登录基本流程处理如下:
1 SecurityContextPersistenceFilter
2 AbstractAuthenticationProcessingFilter
3 UsernamePasswordAuthenticationFilter
4 AuthenticationManager
5 AuthenticationProvider
6 userDetailsService
7 userDetails
8 认证通过
9 SecurityContext
10 SecurityContextHolder
11 AuthenticationSuccessHandler
用户页面登录
1 首先进入 SecurityContextPersistenceFilter 拦截器
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 。。。。。省略 //HttpRequestResponseHolder 对象 HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response); // 判断session是否存在,如果不存在则新建一个session SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder); boolean var13 = false; try { var13 = true; //将securiryContext放入SecurityContextHolder中 SecurityContextHolder.setContext(contextBeforeChainExecution); //调用下一个拦截器,也就是之后所有的拦截器 chain.doFilter(holder.getRequest(), holder.getResponse()); var13 = false; } finally { if (var13) { //获取从context SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext(); //清空SecurityContextHolder 中的contex 临时保存 SecurityContextHolder.clearContext(); //保存后面过滤器生成的数据 到SecurityContextRepository中 this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); request.removeAttribute("__spring_security_scpf_applied"); if (debug) { this.logger.debug("SecurityContextHolder now cleared, as request processing completed"); } } } //从SecurityContextHolder获取SecurityContext实例 8 SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext(); //清空SecurityContextHolder中的SecurityContext SecurityContextHolder.clearContext(); //将SecurityContext实例保存到session中,以便下次请求时候用 9 this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); request.removeAttribute("__spring_security_scpf_applied"); if (debug) { this.logger.debug("SecurityContextHolder now cleared, as request processing completed"); } } }
loadContext 判断session是否存在 没有新建一个
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) { HttpServletRequest request = requestResponseHolder.getRequest(); HttpServletResponse response = requestResponseHolder.getResponse(); //如果session不存在则返回null HttpSession httpSession = request.getSession(false); //根据 private String springSecurityContextKey = "SPRING_SECURITY_CONTEXT"; //获取原来的session SecurityContext context = this.readSecurityContextFromSession(httpSession); if (context == null) { if (this.logger.isDebugEnabled()) { this.logger.debug("No SecurityContext was available from the HttpSession: " + httpSession + ". A new one will be created."); } //如果session 为null 则新建一个 context = this.generateNewContext(); } //将session 保存到 内部类SaveToSessionResponseWrapper 中 HttpSessionSecurityContextRepository.SaveToSessionResponseWrapper wrappedResponse = new HttpSessionSecurityContextRepository.SaveToSessionResponseWrapper(response, request, httpSession != null, context); //保存在 HttpRequestResponseHolder对象中 requestResponseHolder.setResponse(wrappedResponse); if (this.isServlet3) { requestResponseHolder.setRequest(new HttpSessionSecurityContextRepository.Servlet3SaveToSessionRequestWrapper(request, wrappedResponse)); } return context; }
readSecurityContextFromSession 方法中 根据判断session是否存在 会根据 “ SPRING_SECURITY_CONTEXT ”
获取session
用户登录即是认证
登录的实现方式:
2 进入AbstractAuthenticationProcessingFilter类
3 UsernamePasswordAuthenticationFilter 实现了类 AbstractAuthenticationProcessingFilter 调用自身的attemptAuthentication方法
获取用户名和密码,构建token
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (this.postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } else { String username = this.obtainUsername(request); String password = this.obtainPassword(request); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); //构建token ,此时没有进行验证 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); this.setDetails(request, authRequest); // AuthenticationManager 将token传递给 的 authenticate方法 进行 token验证 return this.getAuthenticationManager().authenticate(authRequest); } } //获取密码 protected String obtainPassword(HttpServletRequest request) { return request.getParameter(this.passwordParameter); } //获取账号 protected String obtainUsername(HttpServletRequest request) { return request.getParameter(this.usernameParameter); }
调用authenticate方法,进行token
4 AuthenticationManager 接口
1 AuthenticationManager 接口
public interface AuthenticationManager { Authentication authenticate(Authentication var1) throws AuthenticationException; }
2 ProviderManager 实现了AuthenticationManager 接口
authenticate方法中 result = provider.authenticate(authentication); //返回成功的认证
重写authenticate方法 来获取用户验证信息
5 AuthenticationProvider 接口中方法
Authentication authenticate(Authentication authentication) throws AuthenticationException;
AbstractUserDetailsAuthenticationProvider 实现了AuthenticationProvider 接口
public Authentication authenticate(Authentication authentication) //authentication 传递过来token throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> messages.getMessage( "AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); // Determine username 获取密码 String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName(); boolean cacheWasUsed = true; //从缓存中获取 UserDetails user = this.userCache.getUserFromCache(username); //没有缓存 if (user == null) { cacheWasUsed = false; try { //查询数据库 获取用户账号密码 user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); } catch (UsernameNotFoundException notFound) { logger.debug("User '" + username + "' not found"); //此处 不能抛出异常 UsernameNotFoundException 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); // 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); if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (forcePrincipalAsString) { principalToReturn = user.getUsername(); } //创建成功的认证Authentication return createSuccessAuthentication(principalToReturn, authentication, user); }
retrieveUser 方法查询用户数据:
调用自身的抽象方法
protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException;
DaoAuthenticationProvider 实现类
DaoAuthenticationProvider 继承 AbstractUserDetailsAuthenticationProvider 重写 retrieveUser 方法
用来根据用户名查询用户数据
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { prepareTimingAttackProtection(); try { // 根据用户名查询数据 UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username); if (loadedUser == null) { throw new InternalAuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; } catch (UsernameNotFoundException ex) { mitigateAgainstTimingAttack(authentication); throw ex; } catch (InternalAuthenticationServiceException ex) { throw ex; } catch (Exception ex) { throw new InternalAuthenticationServiceException(ex.getMessage(), ex); } }
6 UserServiceDatils 接口 调用 loadUserByUsername 查询数据
7 userDetails对象返回数据
UserServiceDatils 接口需要自己实现(6.7一起进行)
public class MUserDetailsService implements UserDetailsService { Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private UsersMapper usersMapper; /** *@description: 需要从数据库中通过用户名来查询用户的信息和用户所属的角色 *@author: wangl *@time: 2019/1/8 10:44 *@version 1.0 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { logger.info("===========授权============"); UserDetails userDeatils = null; //通过用户名 查询密码 Users users = usersMapper.selectUserByUserName(username); Set<MGrantedAuthority> authorities = new HashSet<>(); //查询用户角色 if(null != users){ logger.info("用户 = " + users.getUsername() + "----" + users.getPassword()); //查询用户权限 List<Users> usersList = usersMapper.selectRolesAndResourceByUserId(users.getId()); if(null != usersList && usersList.size()>0){ usersList.forEach(user->{ //存放role name 或者 权限名字 authorities.add(new MGrantedAuthority(user.getResourceName())); }); }else{ System.out.println("用户无权限。。"); throw new BadCredentialsException("not found ... "); } 7 返回数据 userDeatils = new Users(users.getUsername(),users.getPassword(),authorities); }else{ throw new BadCredentialsException("用户名不存在"); } return userDeatils; }
查询用户数据之后
返回查询到的loadUser对象 然后用户账号是否被锁定,过期等验证 this.preAuthenticationChecks.check(user);
密码验证
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (authentication.getCredentials() == null) { this.logger.debug("Authentication failed: no credentials provided"); throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } else { //获取密码 String presentedPassword = authentication.getCredentials().toString(); //匹配密码 if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { this.logger.debug("Authentication failed: password does not match stored value"); throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } } }
自定义密码验证器
@Slf4j @Component public class PasswordEncorder implements PasswordEncoder { @Override public String encode(CharSequence charSequence) { log.info("============ charSequence.toString() ============== " + charSequence.toString()); return MD5Util.encode(charSequence.toString()); } @Override public boolean matches(CharSequence charSequence, String s) { String pwd = charSequence.toString(); log.info("前端传过来密码为: " + pwd); log.info("加密后密码为: " + MD5Util.encode(charSequence.toString())); //s 应在数据库中加密 if( MD5Util.encode(charSequence.toString()).equals(MD5Util.encode(s))){ return true; } throw new DisabledException("--密码错误--"); } }
11 AuthenticationSuccessHandler
/** *@description: 自定义登陆成功处理类 *@author: wangl *@time: 2019/1/14 17:46 *@version 1.0 */ @Component public class MyAuthenctiationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { logger.info("登陆成功。。"); String name = authentication.getName(); HttpSession session = request.getSession(); session.setAttribute("user",name); response.sendRedirect("/success"); //super.onAuthenticationSuccess(request, response, authentication); } }
配置类
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired MUserDetailsService mUserDetailsService; //userDetails @Autowired MyFilterSecurityInterceptor myFilterSecurityInterceptor; //自定义拦截器 @Autowired PasswordEncoder passwordEncoder; //密码验证器 @Autowired private MyAuthenctiationFailureHandler myAuthenctiationFailureHandler;//失败处理 @Autowired private MyAuthenctiationSuccessHandler myAuthenctiationSuccessHandler;//成功处理 @Override public void configure(WebSecurity web) throws Exception { web.ignoring().mvcMatchers("/static/**"); //过滤静态资源 } /**定义认证用户信息获取来源,密码校验规则等**/ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //从内存中获取 //明文方式提交 /* auth.inMemoryAuthentication().withUser("zs").password("1234").roles("USER") .and().withUser("admin").password("1234").roles("ADMIN"); //从内存中获取 */ auth.userDetailsService(mUserDetailsService) .passwordEncoder(passwordEncoder); //密码加密方式 /*auth.authenticationProvider(customAuthenticationProvider) .authenticationProvider(authenticationProvider()) //增加 .userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder());*/ /* auth.inMemoryAuthentication().passwordEncoder(new PasswordEncorder()).withUser("zs").password("1234").roles("USER") .and().withUser("admin").password("1234").roles("ADMIN") ;*/ } /**定义安全策略**/ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() ////配置安全策略 .antMatchers("/","/login.html","/loginPage").permitAll() // 定义请求不需要验证 //.antMatchers("/admin/next").hasRole("ADMIN") // 设置只有管理员才能访问的url //.antMatchers("/admin/**").hasAnyRole("ADMIN") // 设置多个角色访问的url .anyRequest().authenticated() //其余所有请求都需要验证 .and() .formLogin() //配置登录页面 .loginPage("/loginPage") //登录页面访问路径 .loginProcessingUrl("/login") //登录页面提交表单路径 .successHandler(myAuthenctiationSuccessHandler)//成功页面 .failureHandler(myAuthenctiationFailureHandler) //失败后跳转路径 .and() .logout() //登出不需要验证 .logoutUrl("/logout").permitAll() // .and() //.authorizeRequests() //.antMatchers("/admin/next").hasRole("ADMIN") //.and() //.rememberMe() //记住我功能 //.tokenValiditySeconds(10000); //自定义的拦截器 , 在适当的地方加入 http.addFilterAt(myFilterSecurityInterceptor, FilterSecurityInterceptor.class); http.csrf().disable(); ///关闭默认的csrf认证 } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
HSV CMYK 转换工具
HSV CMYK互换工具