spring boot + spring Security + redis + token 爬坡
分为几个部分 spring boot 基本配置 controller接口部分 安全校验部分(包括session或者自定义token的形式) redis的token存放与取出 , 数据库校验
spring boot 基本配置
pom和启动类
https://www.cnblogs.com/funkboy/p/12889708.html pom的jar版本要一一对应,不要产生spring冲突
application.yml 和 properties 配置
https://www.cnblogs.com/luzhanshi/p/10597641.html like this
实现检验思路
Security 部分
两种思路,
第一种
一种是spring Security只负责校验 ,生成和存储token的部分放在controller里
这种情况只需要
WebSecurityConfigurer extends WebSecurityConfigurerAdapter configure: http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // JWT Filter 校验部分 在登录的接口配置权限 @PreAuthorize("hasAuthority('ddd:list')")
filter部分
package com.lzw.security.filter; import com.alibaba.fastjson.JSON; import com.lzw.security.common.GenericResponse; import com.lzw.security.common.ServiceError; import com.lzw.security.entity.User; import com.lzw.security.service.SelfUserDetailsService; import com.lzw.security.util.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Set; /** * @author: jamesluozhiwei 组2 * @description: 确保在一次请求只通过一次filter,而不需要重复执行 被springboot security 主类 调用 */ @Component @Slf4j public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Value("${token.expirationMilliSeconds}") private long expirationMilliSeconds; @Autowired SelfUserDetailsService userDetailsService; @Autowired RedisUtil redisUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //? String authHeader = request.getHeader("Authorization"); response.setCharacterEncoding("utf-8"); if (null == authHeader || !authHeader.startsWith("Bearer ")){ filterChain.doFilter(request,response);//token格式不正确 return; } String authToken = authHeader.substring("Bearer ".length()); String subject = JwtTokenUtil.parseToken(authToken);//获取在token中自定义的subject,用作用户标识,用来获取用户权限 //获取redis中的token信息 if (!redisUtil.hasKey(authToken)){ //token 不存在 返回错误信息 response.getWriter().write(JSON.toJSONString(GenericResponse.response(ServiceError.GLOBAL_ERR_NO_SIGN_IN))); return; } //获取缓存中的信息(根据自己的业务进行拓展) HashMap<String,Object> hashMap = (HashMap<String, Object>) redisUtil.hget(authToken); //从tokenInfo中取出用户信息 ******** User user = new User(); user.setId(Long.parseLong(hashMap.get("id").toString())).setAuthorities((Set<? extends GrantedAuthority>) hashMap.get("authorities")); if (null == hashMap){ //用户信息不存在或转换错误,返回错误信息 response.getWriter().write(JSON.toJSONString(GenericResponse.response(ServiceError.GLOBAL_ERR_NO_SIGN_IN))); return; } //更新token过期时间 redisUtil.setKeyExpire(authToken,expirationMilliSeconds); //将信息交给security ******* UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user,null,user.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); filterChain.doFilter(request,response); } }
第二种
另一种全部托管于spring Security
mian class
WebSecurityConfigurer extends WebSecurityConfigurerAdapter
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(customUserDetailsService) .passwordEncoder(passwordEncoder()); } 设置查询方法 customUserDetailsService: //数据库查信息实现 UserDetailsService @Component public class CustomUserDetailsService implements UserDetailsService passwordEncoder() @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
xxxxxxxxxxx
return user;
}
把放入token到redis的任务赋给handler
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler
在filter里
先把token和用户权限存放到redis 并且 public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { ... @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ... UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user,null,user.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); filterChain.doFilter(request,response);
Redis token 部分
在页面调用登录接口时校验 如果数据正确 生成一组redis数据 包含username 权限组 token 等 ,返回token串,接下来所有的请求就要带上这个token ,如果请求符合这个token所代表的用户就放行,如果不符合就会被token过滤,返回失败handler
与前端交互
做好与前端协调,可选全json的数据交换,也可用post body 传值
post 请求
获取值 https://blog.csdn.net/qq_41665356/article/details/80234392
可以用string vo形参和httprequest获取
postman 介绍
https://www.cnblogs.com/zhuxr/p/9009708.html
附赠小提示
controller接口部分
@RestController/Controller 注入 @RequestMapping 等 获取请求与返回
spring PreAuthorize 配置 https://blog.csdn.net/weixin_39220472/article/details/80873268
@RequestMapping("/info/{id}") 参数restful @PreAuthorize("hasRole('sys:config:info')") 权限校验 public R info(@PathVariable("id") Long id){ SysConfig config = sysConfigService.getById(id); return R.ok().put("config", config); }
value: 指定请求的实际地址, 比如 /action/info之类。 method: 指定请求的method类型, GET、POST、PUT、DELETE等 consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html; produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回 params: 指定request中必须包含某些参数值是,才让该方法处理 headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求
R: 一个vo
spring security 爬坡
主要类分析
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter
Security主体类 功能实现基础
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
自定义安全校验 --> AuthenticationManagerBuilder
用于创建AuthenticationManager的SecurityBuilder。允许轻松构建内存身份验证,LDAP身份验证,基于JDBC的身份验证,添加UserDetailsService以及添加AuthenticationProvider。
auth.userDetailsService(customUserDetailsService) customUserDetailsService 这是一个继承UserDetailsService 的Component 重写方法loadUserByUsername 在数据库里校验身份
protected void configure(HttpSecurity http) { 过滤的主方法,核心
antMatchers(urls[String 数组]) 放行的url 数组
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) 关闭session
csrf()
Adds CSRF support. This is activated by default when using WebSecurityConfigurerAdapter's default constructor. You can disable it using:
@Configuration
@EnableWebSecurity
public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() ...; } } Returns: the CsrfConfigurer for further customizations Throws: java.lang.Exception
! 在所有filter 之前
addFilterBefore
public HttpSecurity addFilterBefore(javax.servlet.Filter filter, java.lang.Class<? extends javax.servlet.Filter> beforeFilter) Description copied from interface: HttpSecurityBuilder Allows adding a Filter before one of the known Filter classes. The known Filter instances are either a Filter listed in HttpSecurityBuilder.addFilter(Filter) or a Filter that has already been added using HttpSecurityBuilder.addFilterAfter(Filter, Class) or HttpSecurityBuilder.addFilterBefore(Filter, Class). Specified by: addFilterBefore in interface HttpSecurityBuilder<HttpSecurity> Parameters: filter - the Filter to register before the type beforeFilter beforeFilter - the Class of the known Filter. Returns: the HttpSecurity for further customizations
这个是所有的访问都要过一下在这加过滤token的code
@SneakyThrows
lombok 甩出所有的错误
@EnableWebSecurity
https://blog.csdn.net/andy_zhang2007/article/details/90023901
该注解其实起到了如下效果 :
控制Spring Security是否使用调试模式(通过注解属性debug指定),缺省为false,表示缺省不使用调试模式;
导入 WebSecurityConfiguration,用于配置Web安全过滤器FilterChainProxy;
若干个WebSecurityConfigurerAdapter作用于一个WebSecurity生成一个最终使用的web安全过滤器FilterChainProxy
如果是Servlet 环境,导入WebMvcSecurityConfiguration;
如果是OAuth2环境,导入OAuth2ClientConfiguration;
使用注解@EnableGlobalAuthentication启用全局认证机制
————————————————
版权声明:本文为CSDN博主「安迪源文」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/andy_zhang2007/article/details/90023901
关于 @EnableGlobalMethodSecurity 开启spring
https://www.cnblogs.com/520playboy/p/7286085.html
@bean
注入实例到spring 工厂中以便于@Autowired等获取
没写完呢