spring boot + spring Security + redis token 思路爬坡


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的身份验证,添加UserDetailsS​​ervice以及添加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等获取

 

没写完呢


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM