Spring Boot 3 集成 Spring Security(2)授权

news2024/11/28 4:23:17

文章目录

    • 授权
      • 配置 SecurityFilterChain
      • 基于注解的授权控制
      • 自定义权限决策

在《Spring Boot 3 集成 Spring Security(1)》中,我们简单实现了 Spring Security 的认证功能,通过实现用户身份验证来确保系统的安全性。Spring Security的重要核心功能功能是“认证”和“授权”。接下来我们将深入了解授权机制,看如何控制用户在系统中可以访问的资源和操作。在 Spring Security 中,授权主要基于角色和权限的概念进行控制。

角色(Role):通常用来定义一组权限,用于定义用户身份的层级。比如 ADMIN(ROOT) 角色可能包含管理用户、查看日志等权限。
权限(Authority):具体的操作或资源访问权,则更细粒度地控制用户具体能做什么操作。比如 READ_PRIVILEGES、WRITE_PRIVILEGES 等。

Spring Security 提供了基于角色和权限的访问控制机制,使我们可以轻松管理系统中的授权逻辑。

授权

要实现授权,我们需要在 Spring Security 的配置类中定义用户的角色和访问策略。

代码实现过程:

  • 定义两个用户分别赋予角色 admin -> ROOT、user -> USER
  • 定义Controller 使用不同的用户登录访问接口 AdminController、UserController、SecuredController
  • 登录不同账户验证授权

配置 SecurityFilterChain

package cn.harry.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

/**
 * @author harry
 */
@Configuration
@EnableWebSecurity()
@EnableMethodSecurity(securedEnabled = true) // 开启方法级别的权限控制
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        // 公开访问
                        .requestMatchers("/").permitAll()
                        // 只有 ROOT 角色可以访问 /admin 目录下的资源
                        .requestMatchers("/admin/**").hasRole("ROOT")
                        // USER 和 ROOT 角色都可以访问 /user 目录下的资源
                        .requestMatchers("/user/**").hasAnyRole("ROOT", "USER")
                        // 其他接口需认证
                        .anyRequest().authenticated()
                )

                // 开启基于表单的登录
                .formLogin(Customizer.withDefaults())
//                // 开启注销功能
//                .logout(Customizer.withDefaults())
//                // 开启 HTTP Basic 认证
//                .httpBasic(Customizer.withDefaults())
//                // 开启 CSRF 防护
//                .csrf(Customizer.withDefaults())
//                // 开启跨域资源共享
//                .cors(Customizer.withDefaults())
        ;

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        // 创建用户
        UserDetails admin = User.builder()
                .username("admin")
                .password(passwordEncoder.encode("123456"))
                // 设置用户角色为ROOT
                .roles("ROOT")
                .build();

        UserDetails user = User.builder()
                .username("user")
                .password(passwordEncoder.encode("123456"))
                // 设置用户角色为USER
                .roles("USER")
                .build();

        // 将用户添加到内存中
        manager.createUser(admin);
        manager.createUser(user);
        return manager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 使用 BCrypt 进行密码加密
        return new BCryptPasswordEncoder();
    }
}

代码说明
authorizeHttpRequests():用于定义 URL 路径的访问权限。
requestMatchers("/admin/**").hasRole("ROOT"):指定 /admin/** 下的所有路径都只有 ADMIN 角色的用户可以访问。
requestMatchers("/user/**").hasAnyRole("USER", "ROOT"):允许 USER 和 ROOT 角色访问 /user/** 下的资源。
anyRequest().authenticated():表示系统中的其他请求都需要用户登录后才可以访问。

在 Spring Security 中,角色是权限的一种特殊形式。实际上,hasRole() 是基于 hasAuthority() 实现的。当我们定义角色时,Spring Security 会自动为角色加上前缀 ROLE_,所以 hasRole("ADMIN") 实际上是hasAuthority("ROLE_ADMIN")

基于注解的授权控制

除了在配置类中定义访问策略,Spring Security 还支持使用注解来控制方法的访问权限。常见的注解包括 @PreAuthorize@Secured

  • 使用 @PreAuthorize 注解
    @PreAuthorize 注解可以用于方法级别的权限控制。它可以在方法执行之前检查用户的权限。
@Slf4j
@RestController
public class AdminController {

    @GetMapping("/admin/info")
    @PreAuthorize("hasRole('ROOT')")  // 只有 ADMIN 角色才能访问
    public User adminInfo() {
        // 获取当前登录的用户信息
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        log.info("当前登录的用户信息:{}", user.toString());
        return user;
    }
}
@Slf4j
@RestController
public class UserController {

    @GetMapping("/user/info")
    public User getUserInfo() {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        log.info("当前登录的用户信息:{}", user.toString());
        return user;
    }

    @GetMapping("/user/info2")
    @PreAuthorize("hasRole('USER')") // 只有 USER 角色才能访问
    public User getUserInfo2() {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        log.info("当前登录的用户信息:{}", user.toString());
        return user;
    }
}

使用 user 用户访问 /admin/info时提示无权限,使用 admin 访问则正常打印用户信息。

image-20241126132445673

image-20241126132615673

  • 使用 @Secured 注解
    @Secured 注解也可以实现类似的功能,限制方法访问的权限。

注意:使用 @Secured注解时,需要再 SecurityConfig 文件中添加 @EnableMethodSecurity(securedEnabled = true) // 开启方法级别的权限控制,EnableMethodSecurity源码中看出@Secured默认时关闭状态。

image-20241126113846797

创建一个SecuredController,写一个@Secured("ROLE_USER")才能访问的接口。

@Slf4j
@RestController
public class SecuredController {

    /**
     * 使用 `@Secured`注解时,需要再 SecurityConfig 文件中添加
     	`@EnableMethodSecurity(securedEnabled = true) // 开启方法级别的权限控制`
     * 访问 /secured 需要有 ROLE_USER 权限
     */
    @GetMapping(value = "/secured")
    @Secured("ROLE_USER")
    public User hello() {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        log.info("当前登录的用户信息:{}", user.toString());
        return user;
    }
}

image-20241126133102071

自定义权限决策

在某些场景中,我们可能需要更加灵活的权限控制。在Spring Security中,@PreAuthorize、@PostAuthorize等注解支持SpEL表达式。如果要在表达式中调用其他对象的方法,需要在方法名前加上对象名。例如,@ss.hasPermission('monitor:operlog:list'),其中ss是Spring容器中的一个对象名,hasPermission则是该对象中的方法‌

  • 可以通过定义一个自定义的PermissionService类,并在其中实现权限验证逻辑。

@Service("ss")
public class PermissionService {
    
    public Boolean hasPermission(String... permissions) {
        if (StringUtils.isEmpty(permissions)) {
            return false;
        }
        // 获取当前用户的所有权限
        List<String> perms = SecurityUtils.getUserDetails().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
        // 判断当前用户的所有权限是否包含接口上定义的权限
        return perms.contains(CommonConstant.ALL_PERMISSION) || Arrays.stream(permissions).anyMatch(perms::contains);
    }

}

调用方法:

 	@Operation(summary = "list 分页列表")
    @GetMapping(value = "/list")
    @PreAuthorize("@ss.hasPermission('monitor:operlog:list')")
    public R<IPage<SysOperationLog>> list(Page<SysOperationLog> page, SysOperationLog sysOperationLog) {
        return R.success(sysOperationLogService.page(page, Wrappers.lambdaQuery(sysOperationLog).orderByDesc(SysOperationLog::getCreateTime)));
    }

授权是确保系统安全的重要组成部分,它能帮助我们在系统中根据用户的身份和角色对资源访问进行精细化控制。通过 Spring Security 提供的简单配置和注解支持,我们可以非常灵活地实现授权控制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2248811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Apache OFBiz xmlrpc XXE漏洞(CVE-2018-8033)

目录 1、漏洞描述 2、EXP下载地址 3、EXP利用 1、漏洞描述 Apache OFBiz是一套企业资源计划&#xff08;ERP&#xff09;系统。它提供了广泛的功能&#xff0c;包括销售、采购、库存、财务、CRM等。 Apache OFBiz还具有灵活的架构和可扩展性&#xff0c;允许用户根据业务需求…

【Android】ARouter的使用及源码解析

文章目录 简介介绍作用 原理关系 使用添加依赖和配置初始化SDK添加注解在目标界面跳转界面不带参跳转界面含参处理返回结果 源码基本流程getInstance()build()navigation()_navigation()Warehouse ARouter初始化init帮助类根帮助类组帮助类 completion 总结 简介 介绍 ARouter…

springboot整合hive

springboot整合hive pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.…

IntelliJ IDEA 中,自动导包功能

在 IntelliJ IDEA 中&#xff0c;自动导包功能可以极大地提高开发效率&#xff0c;减少手动导入包所带来的繁琐和错误。以下是如何在 IntelliJ IDEA 中设置和使用自动导包功能的详细步骤&#xff1a; 一、设置自动导包 打开 IntelliJ IDEA&#xff1a; 启动 IntelliJ IDEA 并打…

【MySQL课程学习】:MySQL安装,MySQL如何登录和退出?MySQL的简单配置

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;MySQL课程学习 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 MySQL在Centos 7环境下的安装&#xff1a; 卸载…

Easyexcel(7-自定义样式)

相关文章链接 Easyexcel&#xff08;1-注解使用&#xff09;Easyexcel&#xff08;2-文件读取&#xff09;Easyexcel&#xff08;3-文件导出&#xff09;Easyexcel&#xff08;4-模板文件&#xff09;Easyexcel&#xff08;5-自定义列宽&#xff09;Easyexcel&#xff08;6-单…

(计算机网络)期末

计算机网络概述 物理层 信源就是发送方 信宿就是接收方 串行通信--一次只发一个单位的数据&#xff08;串行输入&#xff09; 并行通信--一次可以传输多个单位的数据 光纤--利用光的反射进行传输 传输之前&#xff0c;要对信源进行一个编码&#xff0c;收到信息之后要进行一个…

uniapp跨域问题解决方案

uniapp跨域问题解决方案 引言 在使用 uni-app 本地开发 H5> 平台时&#xff0c;需要使用浏览器进行调试&#xff0c;而浏览器会有跨域的问题。比如直接通过本地IP地址去访问开发中的页面&#xff0c;同时这个页面会调一些现有的接口时&#xff0c;就面临着跨域的问题。 解决…

Android 基于Camera2 API进行摄像机图像预览

前言 近期博主准备编写一个基于Android Camera2的图像采集并编码为h.264的应用&#xff0c;准备分为三个阶段来完成&#xff0c;第一阶段实现Camera2的摄像机预览&#xff0c;第二阶段完成基于MediaCodec H.264编码&#xff0c;第三阶段完成基于MediaCodec H.264解码,针对不同…

设计模式:11、迭代器模式(游标)

目录 0、定义 1、迭代器模式的四种角色 2、迭代器模式的UML类图 3、示例代码 4、迭代器的next()方法与集合的get(int index)方法的效率对比&#xff08;LinkedList为例&#xff09; 0、定义 提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不需要暴露该对象…

UE5连接VR(pico,quest)进行PC VR开发(没有废话全是干货)

一、PICO VR连接UE 首先picoVR&#xff0c;不管是pico neo3还是pico4&#xff0c;用到的软件就只有三个 分别是pico互联助手PICO 互联 | PICO (picoxr.com)、steam VR&#xff0c;虚幻引擎5 pico互联助手 在pico互联助手中你需要选择两种连接方式&#xff08;推荐USB连接&a…

《UnityShader 入门精要》更复杂的光照

代码&示例图见&#xff1a;zaizai77/Shader-Learn: 实现一些书里讲到的shader 到了这里就开启了书里的中级篇&#xff0c;之后会讲解 Unity 中的渲染路径&#xff0c;如何计算光照衰减和阴影&#xff0c;如何使用高级纹理和动画等一系列进阶内容 Unity 中的渲染路径 在U…

用nextjs开发时遇到的问题

这几天已经基本把node后端的接口全部写完了&#xff0c;在前端开发时考虑时博客视频类型&#xff0c;考虑了ssr&#xff0c;于是选用了nextJs&#xff0c;用的是nextUi,tailwincss,目前碰到两个比较难受的事情。 1.nextUI个别组件无法在服务器段渲染 目前简单的解决方法&…

Golang项目:实现一个内存缓存系统

要求 支持设定过期时间&#xff0c;精确到秒支持设定最大内存&#xff0c;当内存超过时做出合适的处理支持并发安全按照以下接口安全 type Cache interface{//size : 1KB 100KB 1MB 2MB 1GBSetMaxMemory(size string )bool//将value写入缓存Set(key string, val interface{},e…

Softing线上研讨会 | Ethernet-APL:推动数字时代的过程自动化

| &#xff08;免费&#xff09;线上研讨会时间&#xff1a;2024年11月19日 16:00~16:30 / 23:00~23:30 Ethernet-APL以10Mb/s的传输速率为过程工业中的现场设备带来了无缝以太网连接和本质安全电源&#xff0c;这不仅革新了新建工厂&#xff0c;也适用于改造现有工厂。 与现…

《Deep Multimodal Learning with Missing Modality: A Survey》中文校对版

文章汉化系列目录 文章目录 文章汉化系列目录摘要1 引言2 方法论分类&#xff1a;概述2.1 数据处理方面2.2 策略设计方面 3 数据处理方面的方法3.1 模态填充3.1.1 模态组合方法3.1.2 模态生成方法 3.2 面向表示的模型3.2.1 协调表示方法3.2.2 表示组合方法。3.2.3 表示生成方法…

python爬虫案例——猫眼电影数据抓取之字体解密,多套字体文件解密方法(20)

文章目录 1、任务目标2、网站分析3、代码编写1、任务目标 目标网站:猫眼电影(https://www.maoyan.com/films?showType=2) 要求:抓取该网站下,所有即将上映电影的预约人数,保证能够获取到实时更新的内容;如下: 2、网站分析 进入目标网站,打开开发者模式,经过分析,我…

鸿蒙安全控件之位置控件简介

位置控件使用直观且易懂的通用标识&#xff0c;让用户明确地知道这是一个获取位置信息的按钮。这满足了授权场景需要匹配用户真实意图的需求。只有当用户主观愿意&#xff0c;并且明确了解使用场景后点击位置控件&#xff0c;应用才会获得临时的授权&#xff0c;获取位置信息并…

MATLAB矩阵元素的修改及删除

利用等号赋值来进行修改 A ( m , n ) c A(m,n)c A(m,n)c将将矩阵第 m m m行第 n n n列的元素改为 c c c&#xff0c;如果 m m m或 n n n超出原来的行或列&#xff0c;则会自动补充行或列&#xff0c;目标元素改为要求的&#xff0c;其余为 0 0 0 A ( m ) c A(m)c A(m)c将索引…

网络安全之内网安全

下面给出了应对企业内网安全挑战的10种策略。这10种策略即是内网的防御策略&#xff0c;同时也是一个提高大型企业网络安全的策略。 1、注意内网安全与网络边界安全的不同 内网安全的威胁不同于网络边界的威胁。网络边界安全技术防范来自Internet上的攻击&#xff0c;主要是防…