Spring Security 授权

基于request的授权

HttpSecurity 权限配置

    @Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> {authorize// 放行请求:针对含有 admin 权限的用户放行 /user/get 接口.requestMatchers("/users/get").hasAuthority("admin")// 放行请求:针对含有 tourist 权限的用户放行 /get 接口.requestMatchers("/test/auth").hasAuthority("tourist")// 放行请求:针对 ROLE_admin 角色,放行/users/list 接口.requestMatchers("/users/list").hasRole("ROLE_admin")// 对所有用户放行.requestMatchers("/login", "/test/**").permitAll()// 所有的请求都需要授权保护,不授权保护的请求要写在anyRequest之前.anyRequest()// 以认证的会自动授权.authenticated();});http.exceptionHandling(exc -> {// 用户未认证exc.authenticationEntryPoint(new MyAuthenticationSuccessHandler());// 请求被拒绝exc.accessDeniedHandler(new MyAuthenticationSuccessHandler());});// 开启跨域请求http.cors(withDefaults());return http.build();}

在 loadUserByUsername 方法,在查询数据库用户信息的时候,同时查询出用户的权限,这里以角色名代指权限

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {log.info("username:" + username);// 用户名称不唯一,这里的账号名account,不是用户名// 这里user实现了Security的UserDetails对象UsersDO user = usersMapper.selectByAccount(username);if (user == null || StringUtils.isEmpty(user.getPassword())) {throw new UsernameNotFoundException(username + ":用户不存在");}// 获取用户角色列表List<RoleDO> list = usersMapper.selectByAccountRole(username);user.setList(list);return user;}

获取用户信息

@Mapper
public interface UsersMapper extends BaseMapper<Users> {/*** 分页查询*/List<ResUsers> listByPage(Page<ResUsers> page, @Param("param") ReqUsersQuery req);@Select("select ID,ACCOUNT ,USERNAME,PASSWORD ,SEX ,AGE ,PHONE_NUMBER ,DEPARTMENT_NO,DELETED from tm_user  where ACCOUNT =#{account}")UsersDO selectByAccount(@Param("account") String account);@Select("select t1.ACCOUNT ,t2.role_name from  tm_user t1 left join tm_user_role t2 on t1.ACCOUNT =t2.account  \n" +"left  join tm_role t3  on t2.role_name =t3.role_name  where  t1.ACCOUNT =#{account}")List<RoleDO> selectByAccountRole(@Param("account") String username);}

实体类:

@Data
@Slf4j
@TableName("tm_user")
// 实现了Security的UserDetails对象
public class UsersDO implements UserDetails {/*** 账号*/private String id;/*** 账号*/private String account;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 逻辑删除*/@TableLogic@TableField(value = "DELETED", fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)private String deleted;/*** 用户角色列表*/private List<RoleDO> list;/*** 权限列表*/private Set<GrantedAuthority> authorities =new HashSet<>();@Overridepublic boolean isEnabled() {// 通过逻辑删除字段判断是否启用return Integer.valueOf(this.getDeleted()) == 0 ? true : false;}@Overridepublic boolean isAccountNonExpired() {//用户是否未过期return true;}@Overridepublic boolean isCredentialsNonExpired() {// 用户凭证是否未过期return true;}@Overridepublic boolean isAccountNonLocked() {// 用户是否没有被锁定return true;}/*** 获取权限列表,暂时以角色名代表权限* @return*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {// 权限列表for (RoleDO roleDO : list){authorities.add(()->roleDO.getRoleName());}return authorities;}/*** 设置角色 , 这里参考了 Security 的源码* @param roles*/public void roles(String... roles) {for (String role : roles) {// 这里要加上前缀 “ROLE_”Assert.isTrue(!role.startsWith("ROLE_"),() -> role + " cannot start with ROLE_ (it is automatically added)");authorities.add(new SimpleGrantedAuthority("ROLE_" + role));}}}

用户登录后,如果没有对应权限,默认会抛出403异常

基于request的授权

HttpSecurity 权限配置

    @Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> {authorize// 放行请求:针对含有 admin 权限的用户放行 /user/get 接口.requestMatchers("/users/get").hasAuthority("admin")// 放行请求:针对含有 tourist 权限的用户放行 /get 接口.requestMatchers("/test/auth").hasAuthority("tourist")// 放行请求:针对 ROLE_admin 角色,放行/users/list 接口.requestMatchers("/users/list").hasRole("ROLE_admin")// 对所有用户放行.requestMatchers("/login", "/test/**").permitAll()// 所有的请求都需要授权保护,不授权保护的请求要写在anyRequest之前.anyRequest()// 以认证的会自动授权.authenticated();});http.exceptionHandling(exc -> {// 用户未认证exc.authenticationEntryPoint(new MyAuthenticationSuccessHandler());// 请求被拒绝exc.accessDeniedHandler(new MyAuthenticationSuccessHandler());});// 开启跨域请求http.cors(withDefaults());return http.build();}

在 loadUserByUsername 方法,在查询数据库用户信息的时候,同时查询出用户的权限,这里以角色名代指权限

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {log.info("username:" + username);// 用户名称不唯一,这里的账号名account,不是用户名// 这里user实现了Security的UserDetails对象UsersDO user = usersMapper.selectByAccount(username);if (user == null || StringUtils.isEmpty(user.getPassword())) {throw new UsernameNotFoundException(username + ":用户不存在");}// 获取用户角色列表List<RoleDO> list = usersMapper.selectByAccountRole(username);user.setList(list);return user;}

获取用户信息

@Mapper
public interface UsersMapper extends BaseMapper<Users> {/*** 分页查询*/List<ResUsers> listByPage(Page<ResUsers> page, @Param("param") ReqUsersQuery req);@Select("select ID,ACCOUNT ,USERNAME,PASSWORD ,SEX ,AGE ,PHONE_NUMBER ,DEPARTMENT_NO,DELETED from tm_user  where ACCOUNT =#{account}")UsersDO selectByAccount(@Param("account") String account);@Select("select t1.ACCOUNT ,t2.role_name from  tm_user t1 left join tm_user_role t2 on t1.ACCOUNT =t2.account  \n" +"left  join tm_role t3  on t2.role_name =t3.role_name  where  t1.ACCOUNT =#{account}")List<RoleDO> selectByAccountRole(@Param("account") String username);}

实体类:

@Data
@Slf4j
@TableName("tm_user")
// 实现了Security的UserDetails对象
public class UsersDO implements UserDetails {/*** 账号*/private String id;/*** 账号*/private String account;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 逻辑删除*/@TableLogic@TableField(value = "DELETED", fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)private String deleted;/*** 用户角色列表*/private List<RoleDO> list;/*** 权限列表*/private Set<GrantedAuthority> authorities =new HashSet<>();@Overridepublic boolean isEnabled() {// 通过逻辑删除字段判断是否启用return Integer.valueOf(this.getDeleted()) == 0 ? true : false;}@Overridepublic boolean isAccountNonExpired() {//用户是否未过期return true;}@Overridepublic boolean isCredentialsNonExpired() {// 用户凭证是否未过期return true;}@Overridepublic boolean isAccountNonLocked() {// 用户是否没有被锁定return true;}/*** 获取权限列表,暂时以角色名代表权限* @return*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {// 权限列表for (RoleDO roleDO : list){authorities.add(()->roleDO.getRoleName());}return authorities;}/*** 设置角色 , 这里参考了 Security 的源码* @param roles*/public void roles(String... roles) {for (String role : roles) {// 这里要加上前缀 “ROLE_”Assert.isTrue(!role.startsWith("ROLE_"),() -> role + " cannot start with ROLE_ (it is automatically added)");authorities.add(new SimpleGrantedAuthority("ROLE_" + role));}}}

用户登录后,如果没有对应权限,默认会抛出403异常

基于request的授权

HttpSecurity 权限配置

    @Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> {authorize// 放行请求:针对含有 admin 权限的用户放行 /user/get 接口.requestMatchers("/users/get").hasAuthority("admin")// 放行请求:针对含有 tourist 权限的用户放行 /get 接口.requestMatchers("/test/auth").hasAuthority("tourist")// 放行请求:针对 ROLE_admin 角色,放行/users/list 接口.requestMatchers("/users/list").hasRole("ROLE_admin")// 对所有用户放行.requestMatchers("/login", "/test/**").permitAll()// 所有的请求都需要授权保护,不授权保护的请求要写在anyRequest之前.anyRequest()// 以认证的会自动授权.authenticated();});http.exceptionHandling(exc -> {// 用户未认证exc.authenticationEntryPoint(new MyAuthenticationSuccessHandler());// 请求被拒绝exc.accessDeniedHandler(new MyAuthenticationSuccessHandler());});// 开启跨域请求http.cors(withDefaults());return http.build();}

在 loadUserByUsername 方法,在查询数据库用户信息的时候,同时查询出用户的权限,这里以角色名代指权限

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {log.info("username:" + username);// 用户名称不唯一,这里的账号名account,不是用户名// 这里user实现了Security的UserDetails对象UsersDO user = usersMapper.selectByAccount(username);if (user == null || StringUtils.isEmpty(user.getPassword())) {throw new UsernameNotFoundException(username + ":用户不存在");}// 获取用户角色列表List<RoleDO> list = usersMapper.selectByAccountRole(username);user.setList(list);return user;}

获取用户信息

@Mapper
public interface UsersMapper extends BaseMapper<Users> {/*** 分页查询*/List<ResUsers> listByPage(Page<ResUsers> page, @Param("param") ReqUsersQuery req);@Select("select ID,ACCOUNT ,USERNAME,PASSWORD ,SEX ,AGE ,PHONE_NUMBER ,DEPARTMENT_NO,DELETED from tm_user  where ACCOUNT =#{account}")UsersDO selectByAccount(@Param("account") String account);@Select("select t1.ACCOUNT ,t2.role_name from  tm_user t1 left join tm_user_role t2 on t1.ACCOUNT =t2.account  \n" +"left  join tm_role t3  on t2.role_name =t3.role_name  where  t1.ACCOUNT =#{account}")List<RoleDO> selectByAccountRole(@Param("account") String username);}

实体类:

@Data
@Slf4j
@TableName("tm_user")
// 实现了Security的UserDetails对象
public class UsersDO implements UserDetails {/*** 账号*/private String id;/*** 账号*/private String account;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 逻辑删除*/@TableLogic@TableField(value = "DELETED", fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)private String deleted;/*** 用户角色列表*/private List<RoleDO> list;/*** 权限列表*/private Set<GrantedAuthority> authorities =new HashSet<>();@Overridepublic boolean isEnabled() {// 通过逻辑删除字段判断是否启用return Integer.valueOf(this.getDeleted()) == 0 ? true : false;}@Overridepublic boolean isAccountNonExpired() {//用户是否未过期return true;}@Overridepublic boolean isCredentialsNonExpired() {// 用户凭证是否未过期return true;}@Overridepublic boolean isAccountNonLocked() {// 用户是否没有被锁定return true;}/*** 获取权限列表,暂时以角色名代表权限* @return*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {// 权限列表for (RoleDO roleDO : list){authorities.add(()->roleDO.getRoleName());}return authorities;}/*** 设置角色 , 这里参考了 Security 的源码* @param roles*/public void roles(String... roles) {for (String role : roles) {// 这里要加上前缀 “ROLE_”Assert.isTrue(!role.startsWith("ROLE_"),() -> role + " cannot start with ROLE_ (it is automatically added)");authorities.add(new SimpleGrantedAuthority("ROLE_" + role));}}}

用户登录后,如果没有对应权限,默认会抛出403异常

请添加图片描述

可以自定义请求被拒绝的返回结果:

@Configuration
@Slf4j
public class MyAuthenticationSuccessHandler implements  AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {log.info("请求被拒绝-------------->");response.setContentType("application/json;charset=UTF-8");Map<String, Object> res = new HashMap<>();res.put("code", "-1");res.put("message", "请求被拒绝");Map<String, Object> data = new HashMap<>();res.put("data", data);response.getWriter().println(JSONUtil.toJsonStr(res));}}

RBAC模型

权限模型

  • RBAC(Role Based Access Controll):基于角色的权限控制,简单来说就是:用户-角色-权限-资源,涉及的表有:
    • 用户(t_user)
    • 用户_角色(t_user_role)【N对N关系需要中间表】
    • 角色(t_role)
    • 角色_权限(t_role_perm)
    • 权限(t_permission)
  • ACL(Access Controll List):基于用户的权限控制,涉及的表有:
    • 用户(t_user)
    • 用户_权限(t_user_perm)
    • 权限(t_permission)

通过 RBAC 获取到用户的具体权限后,再通过 Security 的 用户-权限-资源 来进行权限控制

// 针对某个资源,对某个权限来放行
.requestMatchers("/users/get").hasAuthority("function_id")

基于方法的授权

使用注解,针对某个方法开启授权保护

开启配置:

@Configuration
// 开启spring Security的自定义配置,在springBoot项目中可以省略此注解,因为Security-stater默认开启了自定义配置
@EnableWebSecurity
// 开启基于方法的授权
@EnableMethodSecurity
public class SecurityConfig {}

给方法增加权限保护

    @PreAuthorize("hasRole('admin')")@GetMapping("/apply")public BaseResultModel apply(){return BaseResultModel.success("授权返回成功");}

注解及含义

  • @PostAuthorize 在目标方法执行之后进行权限校验
  • @PostFilter 在目标方法执行之后对返回结果进行过滤
  • @PreAuthorize 在目标方法执行之前进行权限校验
  • @PreFilter 在目标方法执行之前对方法参数进行过滤
  • @Secured 访问目标方法必须具备对应的角色
  • @DenyAll 拒绝所有访问
  • @PermitAll 允许所有访问
  • @RolesAll 访问目标方法必须具备对应的角色

授权的原理分析

  • ConfigAttribute在springsecurity中,用户请求一个资源(通常是一个接口或者Java方法)需要的角色会被封装成一个ConfigAttribute对象,在ConfigAttribute中只有一个getAttribute方法,该方法赶回一个String字符串(角色名称)。一般的角色名称都带有一个ROLE_前缀,投票器AccessDecisionVoter所做的事情,其实就是比较用户所具有的角色和请求某个资源所需要的ConfigAttribute之间的关系。
  • AccessDecisionVoter和AccessDecisionManager都有众多实现类。在AccessDecisionManager中会挨个遍历AccessDecisionVoter,进而决定是否允许用户方法,因而AccessDecisionVoter和AccessDecisionManager俩者关系类似于AuthenticationProvicder和ProviderManager的关系。

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

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

相关文章

【Druid 未授权访问漏洞】解决办法

【Druid 未授权访问漏洞】解决办法 漏洞描述 Alibaba Druid 未授权访问【原理扫描】 Alibaba Druid是一款Java语言开发的数据库连接池。Druid能够提供强大的监控和扩展功能。 Alibaba Druid 默认情况下未设置访问控制&#xff0c;攻击者可以登录以获取敏感信息 修改web.xml…

深入理解 React 18 中的 memo 和 useCallback:优化你的组件性能

深入理解 React 18 中的 memo 和 useCallback&#xff1a;优化你的组件性能 在现代前端开发中&#xff0c;性能优化是一个永恒的话题。React 18 引入了一些新的特性和改进&#xff0c;使得性能优化变得更加容易和高效。今天&#xff0c;我们将深入探讨两个非常重要的 Hooks&am…

UE4-光照渲染、自动曝光、雾

目录 一.光源种类 二.灯光的移动性 三.自动曝光 四.指数级高度雾 五.实现光束 一.光源种类 1.定向光源 用来模拟现实中的太阳光。 2.点光源 比如现实中的灯泡 3.聚光源 4.矩形光源 是这几个光源中性能开销最大的&#xff0c;一般不用到游戏场景中&#xff0c;因为游…

【文心智能体】前几天百度热搜有一条非常有趣的话题《00后疯感工牌》,看看如何通过低代码工作流方式实现图片显示

00后疯感工牌体验&#xff1a;https://mbd.baidu.com/ma/s/6yA90qtM 目录 前言比赛推荐工作流创建工作流入口创建工作流界面工作流界面HTTP工具卡点地方 总结推荐文章 前言 前几天百度热搜有一条非常有有趣《00后疯感工牌》。 想着通过文心智能体去一键生成00后疯感工牌是不是…

Qt 多语言

记录Qt多语言的实现过程 目录 1.项目配置文件.pro配置 2.程序中的字符串用tr()封装 3.生成翻译文件 4.使用Qt语言家修改翻译文件 4.1使用Qt语言家打开 4.2 .更改文件配置 5. 生成qm文件 6.代码执行切换语言 6.1入口处 6.2 事件执行 0.效果 1.项目配置文件.pro配置 T…

js执行机制----事件循环

前言 问题 一般情况下,我们都认为js是顺序执行的 但是遇到下列情况 setTimeout(function(){console.log(定时器开始啦) });new Promise(function(resolve){console.log(马上执行for循环啦);for(var i 0; i < 10000; i){i 99 && resolve();} }).then(function(…

AI技术在企业招聘中的应用案例分析

一、引言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;其在企业招聘领域的应用也越来越广泛。AI技术不仅改变了传统的招聘流程&#xff0c;还提高了招聘的效率和准确性&#xff0c;为企业招聘人员提供了更加便捷、高效的方式。本文将以某大型互联网公司…

opencv—常用函数学习_“干货“_2

目录 五、矩阵计算函数 归一化矩阵 (normalize) 转置矩阵 (transpose) 求矩阵的逆 (invert) 翻转矩阵 (flip) 旋转矩阵 (rotate) 求矩阵的行列式 (determinant) 求矩阵的迹 (trace) 求矩阵的特征值和特征向量 (eigen) 六、代数运算 矩阵加法 (add) 矩阵减法 (subtra…

通过手机控制家用电器的一个程序的设计(一)

一、概述 设计一款安卓平台上的家庭智能控制软件&#xff0c;通过语音识别指令控制家用电器。该软件结合离线语音识别技术、红外线和WIFI通讯技术&#xff0c;实现对家电的智能控制&#xff0c;如开关机、调温度、调频道等操作。 二、主要功能模块 离线语音识别模块 功能&…

嵌入式人工智能(7-树莓派4B的IIC总线连接OLED显示中文与图片)

1、IIC总线 IIC总线&#xff08;Inter-Integrated Circuit&#xff09;是一种串行通信总线&#xff0c;也被称为I2C总线。它由飞利浦&#xff08;Philips&#xff09;公司在1980年代开发&#xff0c;用于连接微处理器和外部设备。 IIC总线使用两根信号线&#xff1a;SDA&…

删除windows系统里磁盘的恢复分区

说下我的情况 我买了块固态磁盘&#xff0c;插上主板&#xff0c;发现它自带了系统&#xff0c;这样我开机就会转到这块磁盘&#xff0c;即使在boot里改变也不行&#xff0c;后面我格式化了对应的盘符&#xff0c;但在磁盘管理里&#xff0c;发现有个EFI系统分区和恢复分区存在…

C++中的语句详细介绍:简单语句、条件、循环迭代语句、跳转语句、异常处理语句、try语句等

文章目录 C中的语句(1)简单语句A.空语句B.复合语句 (2)条件语句(3)迭代语句A.常规for循环B.范围for循环C.while和do...while (4)跳转语句A.break语句B.continue语句C.goto语句 (5)异常处理语句A.标准异常B.throw抛出异常 (6)try语句 C中的语句 (1)简单语句 简单语句包括&#…

卷麻了!字节软件测试四轮面试,成功拿到offer啦!

❗️❗️字节面试攻略❗️❗️ ⭕️招聘特点&#xff1a;关注可复制直接上手的人才&#xff01; 字节是人才扎堆的地方&#xff0c;人来就马上能做&#xff0c;只招现成的人才。 字节软件测试的面试题同样也是分了四轮&#xff0c;成功拿到ooffer了&#xff0c;给大家总结一下每…

MyBatis源码中的设计模式1

1. 建造者模式的应用 建造者模式属于创建类模式&#xff0c;通过一步一步地创建一个复杂的对象&#xff0c;能够将部件与其组装过程分开。用户只需指定复杂对象的类型&#xff0c;就可以得到该对象&#xff0c;而不需要了解其内部的具体构造细节。《Effective Java》中也提到&…

Django captcha 验证

1.安装模块 pip install django-simple-captcha pip install Pillow2.在settings中&#xff0c;将captcha注册到app列表里 # MxOnline/settings.py INSTALLED_APPS [# 图片登陆验证captcha, ]3.captcha需要在数据库中建立自己的数据表&#xff0c;所以需要执行migrate命令生…

捷配PCB打样采用机械盲埋孔制造,有何优势?

在电子制造领域&#xff0c;盲孔&#xff08;Blind Vias&#xff09;与埋孔&#xff08;Buried Vias&#xff09;是两种关键的PCB&#xff08;印刷电路板&#xff09;过孔技术。盲孔特指那些连接内层走线至外层走线的过孔&#xff0c;但并不贯穿整个板体。相对地&#xff0c;埋…

镜舟科技荣获优秀数字化服务商奖,助力企业用数智技术重塑新消费

7 月 13 日&#xff0c;由 ITShare智享会和 BT商业科技观察主办的2024 第八届 FMCG 零售消费品数字化峰会于上海落幕。在现场&#xff0c;镜舟科技凭借在多家零售企业构建与实施智能数据中台解决方案的成功经验&#xff0c;荣获优秀数字化服务商奖项。 在会上&#xff0c;麦当劳…

力扣第十二题——整数转罗马数字

内容介绍 七个不同的符号代表罗马数字&#xff0c;其值如下&#xff1a; 符号值I1V5X10L50C100D500M1000 罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则&#xff1a; 如果该值不是以 4 或 9 开头&#xff0c;请选择可以从输入中…

云动态摘要 2024-07-16

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 数据库上云优选 阿里云 2024-07-04 RDS、PolarDB、Redis、MongoDB 全系产品新用户低至首年6折起&#xff01; [免费体验]智能助手ChatBI上线 腾讯云 2024-07-02 基于混元大模型打造&…

C语言 ——— 编写代码,判断 整型数组 是否 有序

目录 题目要求 代码实现 题目要求 判断 整型数组 是否有序 如果 整型数组 有序输出 sorted&#xff1b;否则输出 unsorted 代码实现 #include<stdio.h> int main() {int arr[10] { 0 };int sz sizeof(arr) / sizeof(arr[0]);//输入for (int i 0; i < sz; i){s…