当前位置: 首页 > article >正文

SpringSecurity Demo实操

Builder链式添加测试数据

如果只是想测试某一项技术,但是又不想通过数据库去写查询接口来操作,就可以在项目启动之初初始化一个Map或ArrayList来模拟数据库,下面具体操作代码:

     /**
     * 存放默认用户信息
     */
    private List<AdminUserDetails> adminUserDetailsList = new ArrayList<>();
    /**
     * 存放默认资源信息
     */
    private List<UmsResource> resourceList = new ArrayList<>();
    @Autowired
    private PasswordEncoder passwordEncoder;

    // todo 后续改为用数据库查数据
    @PostConstruct
    private void init(){
        adminUserDetailsList.add(AdminUserDetails.builder()
                .username("admin")
                .password(passwordEncoder.encode("123456"))
                .authorityList(CollUtil.toList("1:brand:create","2:brand:update","3:brand:delete","4:brand:list","5:brand:listAll"))
                .build());
        adminUserDetailsList.add(AdminUserDetails.builder()
                .username("macro")
                .password(passwordEncoder.encode("123456"))
                .authorityList(CollUtil.toList("5:brand:listAll"))
                .build());
        resourceList.add(UmsResource.builder()
                .id(1L)
                .name("brand:create")
                .url("/brand/create")
                .build());
        resourceList.add(UmsResource.builder()
                .id(2L)
                .name("brand:update")
                .url("/brand/update/**")
                .build());
        resourceList.add(UmsResource.builder()
                .id(3L)
                .name("brand:delete")
                .url("/brand/delete/**")
                .build());
        resourceList.add(UmsResource.builder()
                .id(4L)
                .name("brand:list")
                .url("/brand/list")
                .build());
        resourceList.add(UmsResource.builder()
                .id(5L)
                .name("brand:listAll")
                .url("/brand/listAll")
                .build());
    }

比较有意思的是这里用到了builder链式填充的形式,可以看定义类的写法:

@Data
@EqualsAndHashCode(callSuper = false)
@Builder
public class AdminUserDetails implements UserDetails {
    private String username;
    private String password;
    private List<String> authorityList;
}

这里需要先加上注解@Builder才能支持链式调用。

言归正传,回到SpringSecurity,这里介绍下这里的业务场景:模拟一个登录请求,其中authorityList是权限列表,存储当前用户具备的一些权限(注意这里的写法同SpringSecurity这个方法的参数保持一致)

然后这个类它是继承UserDetails,这个是属于SpringSecurity其下的,它包含一些对账号密码的常用API方法,更为安全吧,直接重写即可:

SpringSecurity最核心的就是要补充完善这样一个配置类,下面是一个案例:

@Configuration
public class SecurityConfig {

    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;
    // 处理器,拒绝策略
    @Autowired
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    private DynamicSecurityService dynamicSecurityService;
    @Autowired
    private DynamicSecurityFilter dynamicSecurityFilter;

    // 配置过滤器等其他配置
    @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
        .authorizeRequests();
        //不需要保护的资源路径允许访问
        for (String url : ignoreUrlsConfig.getUrls()) {
            registry.antMatchers(url).permitAll();
        }
        //允许跨域请求的OPTIONS请求
        registry.antMatchers(HttpMethod.OPTIONS)
        .permitAll();
        httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf
        .disable()
        .sessionManagement()// 基于token,所以不需要session
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
        .anyRequest()// 除上面外的所有请求全部需要鉴权认证
        .authenticated();
        // 禁用缓存
        httpSecurity.headers().cacheControl();
        // 添加JWT filter(确定过滤器的顺序)
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        //添加自定义未授权和未登录结果返回
        httpSecurity.exceptionHandling()
        .accessDeniedHandler(restfulAccessDeniedHandler)
        .authenticationEntryPoint(restAuthenticationEntryPoint);
        //有动态权限配置时添加动态权限校验过滤器
        if(dynamicSecurityService!=null){
            registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
        }
        return httpSecurity.build();
    }

}

在配置类中,可以发现它是按照顺序依次进行处理的,就很像日常写的业务逻辑处理,然后其中需要用到什么组件就去新增即可,比较熟悉的就是添加过滤器,这里是两个:JWT校验和权限校验

登录流程:

用户成功登录,后台依靠jwt为用户生成一个密钥,之后携带在每次请求头中。我们知道jwt是无状态的,首先肯定是有个过滤器,对所有请求进行校验(当然也需要为登录注册接口设置白名单),之后的所有请求都会通过过滤器进行处理,过滤器核心逻辑就是校验jwt是否有效。

在选择继承的过滤器,建议选用 OncePerRequestFilter , 确保 在每次请求中只被执行 一次( 有时在复杂的请求链中,过滤器可能会因为转发或重定向等操作被重复调用)

public class MyCustomFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        // 执行过滤逻辑,比如检查请求头、设置属性、日志记录等
        System.out.println("Executing custom filter logic");

        // 调用过滤器链中的下一个过滤器或最终的资源处理器
        filterChain.doFilter(request, response);
    }
}

上面是使用jwt来保证了用户会话的持久性,但不同角色的权限控制怎么实现呢?下面就该SpringSecurity上场了。

------未完待续

SpringSecurity权限控制


http://www.kler.cn/a/392133.html

相关文章:

  • 《新智慧》期刊的征稿范围主要包括哪些方面?
  • 使用pdfjs加载多页pdf并实现打印
  • 使用 Visual Studio Installer 彻底卸载 Visual Studio方法与下载
  • GISBox VS ArcGIS:分别适用于大型和小型项目的两款GIS软件
  • jenkins提交gitee后自动部署
  • 天才的懈怠 : 平衡二叉树
  • 【系统架构设计师】真题论文: 论软件可靠性设计与应用(包括解题思路和素材)
  • Spring Boot编程训练系统:实战开发技巧
  • Normal-GS: 3D Gaussian Splatting with Normal-Involved Rendering 论文解读
  • 【vue】echarts地图添加蒙版图片,多图层地图实现天气信息展示
  • Hadoop生态圈框架部署(六)- HBase完全分布式部署
  • λ矩阵与矩阵的Jordan标准形
  • 蓝牙BLE开发——iOS 每次写入数据超过200字节报错?
  • CSS教程(八)- 盒子模型
  • Oracle的字符串函数
  • 解决:this is incompatible with sql_mode=only_full_group_by
  • 动态规划---解决多段图问题
  • BERT框架详解
  • JavaScript判断是否是有效字符串
  • Webpack 中无法解析别名路径的原因及解决方案
  • Unet++改进20:添加RFAConv||用于特征冗余的空间和通道重构卷积
  • Pinia
  • 相亲小程序(源码+文档+部署+讲解)
  • sql专题 之 count()区别
  • 数据安全、信息安全、网络安全区别与联系
  • sql专题 之 sql的执行顺序