SpringSecurity+Redis认证过程小结
作者:ME_._ME
由于今天用Security进行权限管理的时候出现了一些Bug,特此发这篇博客来补习一下对SpringSecurity的理解
前言引入
当今市面上用于权限管理的流行的技术栈组合是
- ssm+shrio
- SpringCloud+SpringBoot+SpringSecurity
这种搭配自然有其搭配的特点,由于SpringBoot的自动注入配置原理,在创建项目时就自动注入管理SpringSecurity的过滤器容器(DelegatingFilterProxy),而这个过滤器是整个SpringSercurity的核心。掌握着SpringSercurity整个权限认证过程,而SpringBoot很香的帮你将其自动注入了,而用ssm
去整合Security,将会耗用大量的配置文件,不易于开发,而Security的微服务权限方案,更是能和Cloud完美融合,于是Security比Shrio更强大,功能更齐全。
Security的核心配置文件
核心:Class SecurityConfig extends WebSecurityConfigurerAdapter
继承了WebSecurityConfigurerAdapter后我们关注于configure方法对于在整个安全认证的过程进行相关的配置,当然在配置之前我们先简单了解一下流程
简单的看了整个权限认证的流程,很轻易的总结得出,SpringSecurity核心的就是以下几种配置项了
- 拦截器(Interceptor)
- 过滤器(Filter)
- 处理器(Handler,异常处理器,登录成功处理器)
那我们就首先通过配置来完成认证过程吧!!!!
Security的认证过程
假设我们要实现一下的认证功能
1. 是登录请求
- 我们需要先判断验证码是否正确(验证码过滤器,通过addFilerbefore实现前置拦截)
- 再判断用户名密码是否正确(使用自带的用户名密码过滤器,UsernamePasswordAuthenticationFilter)
- 配置异常处理器(Handler)通过IO流将异常信息写出
关于密码校验的流程:
UsernamePasswordAuthenticationFilter的密码校验规则是基于AuthenticationManagerBuilder(认证管理器)下的 UserDetailsService里的规则进行验证的:
其中的核心方法:
1.public UserDetails *loadUserByUsername(String username)
通过请求参数的用户名去数据库查询是否存在,存在则将其封装在UserDetails里面,而验证过程是通过AuthenticationManagerBuilder获取到UserDetail里的username和password来校验的,
这样我们就可以通过
- 配置yaml文件设置账号密码
- 通过数据库结合UserDetail来设置账号密码
(UserDetailsService中的方法,注意需要将UserDetailsService注入AuthenticationManagerBuilder中)
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser sysUser = sysUserService.getByUsername(username); if (sysUser == null) { throw new UsernameNotFoundException("用户名或密码不正确"); } // 注意匹配参数,前者是明文后者是暗纹 System.out.println("是否正确"+bCryptPasswordEncoder.matches("111111",sysUser.getPassword())); return new AccountUser(sysUser.getId(), sysUser.getUsername(), sysUser.getPassword(), getUserAuthority(sysUser.getId())); }
通过了这个验证后,过滤器放行,不通过就用自定义或者默认的处理器处理
核心配置文件:
package com.markerhub.config; import com.markerhub.security.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired LoginFailureHandler loginFailureHandler; @Autowired LoginSuccessHandler loginSuccessHandler; @Autowired CaptchaFilter captchaFilter; @Autowired JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired JwtAccessDeniedHandler jwtAccessDeniedHandler; @Autowired UserDetailServiceImpl userDetailService; @Autowired JwtLogoutSuccessHandler jwtLogoutSuccessHandler; @Bean JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception { JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager()); return jwtAuthenticationFilter; } @Bean BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } private static final String[] URL_WHITELIST = { "/login", "/logout", "/captcha", "/favicon.ico", }; protected void configure(HttpSecurity http) throws Exception { http.cors().and().csrf().disable() // 登录配置 .formLogin() .successHandler(loginSuccessHandler) .failureHandler(loginFailureHandler) .and() .logout() .logoutSuccessHandler(jwtLogoutSuccessHandler) // 禁用session .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 配置拦截规则 .and() .authorizeRequests() .antMatchers(URL_WHITELIST).permitAll() .anyRequest().authenticated() // 异常处理器 .and() .exceptionHandling() .authenticationEntryPoint(jwtAuthenticationEntryPoint) .accessDeniedHandler(jwtAccessDeniedHandler) // 配置自定义的过滤器 .and() .addFilter(jwtAuthenticationFilter()) .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class) ; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailService); } }
2. 不是登录请求
- 通过JwtfFilter来查看是否为登录状态
使用Redis整合时的注意事项
本质上还是编写过滤器链:
- 在登录请求前添加过滤器
- 注意验证码存储在redis的失效时间,如果超过失效时间将会被验证码拦截器拦截下来
- 需要准备一个生成验证码的接口,存储在Redis中
- 使用完验证码需要将其删除
// 校验验证码逻辑 private void validate(HttpServletRequest httpServletRequest) { String code = httpServletRequest.getParameter("code"); String key = httpServletRequest.getParameter("token"); if (StringUtils.isBlank(code) || StringUtils.isBlank(key)) { System.out.println("验证码校验失败2"); throw new CaptchaException("验证码错误"); } System.out.println("验证码:"+redisUtil.hget(Const.CAPTCHA_KEY, key)); if (!code.equals(redisUtil.hget(Const.CAPTCHA_KEY, key))) { System.out.println("验证码校验失败3"); throw new CaptchaException("验证码错误"); } // 一次性使用 redisUtil.hdel(Const.CAPTCHA_KEY, key); }
到此这篇关于SpringSecurity+Redis认证过程总结的文章就介绍到这了,更多相关SpringSecurity Redis认证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!