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,一经查实,立即删除!

相关文章

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(…

嵌入式人工智能(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》中也提到&…

捷配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;麦当劳…

云动态摘要 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…

线程控制

对线程的控制思路和进程相似&#xff0c;创建、等待、终止&#xff0c;只需要调用接口就行。但是在Linux下没有线程的概念&#xff0c;因为Linux的设计者认为&#xff0c;线程是一种轻量级的进程&#xff0c;毕竟创建线程只需要创建PCB。因此Linux中使用多线程必须使用第三方pt…

Spring MVC入门2

Postman的使用 接上期我们抛出了一个问题&#xff0c;Postman的使用 可以点击链接下载 https://www.postman.com/downloads/ 安装之后会提示版本升级&#xff0c;直接点击dissmiss即可。 要想发送数据&#xff0c;具体歩奏如下简图&#xff1a; 还有一个更具体的图&#xff…

使用GDAL(C++库)从末尾行开始向上读取图像数据

使用GDAL&#xff08;C库&#xff09;从末尾行读取图像数据 OpenCV等图像库默认的读取方式都是从第一行开始&#xff0c;逐行读取数据&#xff08;自顶向下&#xff09;&#xff0c;填充到内存缓冲区&#xff1b;对于某些特殊应用&#xff0c;需要反行序读取&#xff08;从末尾…

朴素模式匹配算法与KMP算法(非重点)

目录 一. 朴素模式匹配算法1.1 什么是字符串的匹配模式1.2 朴素模式匹配算法1.3 通过数组下标实现朴素模式匹配算法 二. KMP算法2.1 算法分析2.2 用代码实现&#xff08;只会出现在选择题&#xff0c;考察代码的概率不大&#xff09; 三. 手算next数组四. KMP算法的进一步优化4…

在AWS创建一台Windows主机并登录

正文共&#xff1a;1111 字 21 图&#xff0c;预估阅读时间&#xff1a;1 分钟 因为之前微软云Azure免费&#xff0c;我们还做了简单的测试&#xff08;白嫖党618福利&#xff01;来Azure领200美刀&#xff01;外加云主机免费用一年&#xff01;&#xff09;&#xff1b;并且通…

k8s核心操作_存储抽象_K8S中使用Secret功能来存储密码_使用免密拉取镜像_k8s核心实战总结---分布式云原生部署架构搭建033

注意在看的时候一定要把 dxxxx中的xxxx换成--o----c----k----e----r 然后我们再来看一个k8s中的secret的功能,这个功能 用来存储密码的,configMap是用来存配置的 比如我们有个pod,他的镜像,如果是需要密码的,那么 我们现在是从公共仓库拉取的,如果我们从私有仓库拉取,有密码…

从 Icelake 到 Iceberg Rust

本文作者丁皓是Databend 研发工程师&#xff0c;也是 ASF Member&#xff0c; Apache OpenDAL PMC Chair &#xff0c;主要研究领域包括存储、自动化与开源。 太长不看 Icelake 已经停止更新&#xff0c;请改用 iceberg-rust。 Iceberg-rust 是一个由社区驱动的项目&#xff0…