【Spring Security】打造安全无忧的Web应用--使用篇

🥳🥳Welcome Huihui's Code World ! !🥳🥳

接下来看看由辉辉所写的关于Spring Security的相关操作吧 

目录

🥳🥳Welcome Huihui's Code World ! !🥳🥳

一.Spring Security中的授权是什么

二. 基于单体项目的授权

1.修改User配置角色和权限

2.修改SpringSecurity配置类

3.测试

三.基于前后端分离项目的授权 

JsonResponseBody

JsonResponseStatus

修改SpringSecurity配置类


在正式讲解今天的内容之前,我们还需要了解一个东西--RBAC数据模型,RBAC(Role-Based Access Control,基于角色的访问控制)数据模型是一种访问控制模型,它使用角色作为访问控制的基本单元。在这个模型中,用户被分配到不同的角色,每个角色代表着一组操作或任务。系统管理员将访问权限授予角色,而不是直接授予给用户。当用户需要执行某个操作时,系统可以检查该用户是否拥有执行此操作所需的角色,并根据角色的权限来授权或拒绝访问。RBAC 数据模型可以有效地管理复杂的访问控制策略,提高系统安全性和管理效率。

遇到复杂的权限管理,都是配置在数据库中的,一般由五张表组成,即RBAC。

  1)RBAC数据模型的五张表

          用户表,存储用户信息,由业务人员维护;

          角色表,存储角色信息,由业务人员维护 ;

          资源表,存储资源信息(菜单、按钮及其URL),由开发人员维护;

          用户-角色关系表,存储用户和角色的对应关系,多对多,由业务人员维护;

          角色-资源关系表,存储角色和资源的对应关系 ,多对多,由业务人员维护;

一.Spring Security中的授权是什么

        在Spring Security中,授权是指系统根据用户的角色和权限决定用户是否有权访问特定的资源或执行特定的操作。

        Spring Security提供了多种授权机制,包括基于角色的授权和基于权限的授权

                基于角色的授权是指系统将用户分配到不同的角色,每个角色具有不同的权限,然后通过判断用户是否具有特定的角色来进行授权。例如,系统可以定义"ADMIN"角色和"USER"角色,管理员拥有更高的权限,可以访问和操作更多的资源,而普通用户只能访问受限资源。

                基于权限的授权是指系统将具体的权限授予用户,用户可以直接拥有某个权限,而不需要通过角色间接获得。例如,系统可以定义"READ"、"WRITE"、"DELETE"等权限,根据用户所拥有的权限判断其是否有权对资源进行读取、写入或删除操作。

        Spring Security还支持细粒度的授权控制,可以通过注解、表达式或配置文件等方式来定义授权规则。可以使用@PreAuthorize@PostAuthorize@Secured等注解来标注方法或类,以声明访问资源的权限要求。

        通过授权机制,Spring Security可以保护应用程序免受未经授权的访问,确保只有具备合适权限的用户才能访问敏感资源,提高应用程序的安全性

二. 基于单体项目的授权

1.修改User配置角色和权限

这里也可以使用多表联查的方式去做,不过考虑到在数据量过于庞大的情况下,我们常常会进行分库分表,所以我们这里采用的是流的形式来编写的

package com.wh.security.config;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wh.security.model.*;
import com.wh.security.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;/*** @author是辉辉啦* @create 2023-12-23-14:21*/
@Component
public class MyUserDetailsService implements UserDetailsService {@Autowiredprivate IUserService userService;@Autowiredprivate IUserRoleService userRoleService;@Autowiredprivate IRoleService roleService;@Autowiredprivate IRoleModuleService roleModuleService;@Autowiredprivate IModuleService moduleService;/*** 实现Spring Security内置的UserDetailService接口,重写loadUserByUsername方法实现数据库的身份校验* @param username* @return* @throws UsernameNotFoundException*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据用户名查询数据库中用户信息User user = userService.getOne(new QueryWrapper<User>().eq("username", username));//判断用户是否存在if(Objects.isNull(user)) {throw new UsernameNotFoundException("该用户不存在!!!");}//先拿到用户的身份idList<Integer> roleIds = userRoleService.list(new QueryWrapper<UserRole>().eq("user_id", user.getId())).stream().map(UserRole::getRoleId).collect(Collectors.toList());//根据身份id再拿到身份所对应的角色名称roleNameList<String> roleName = roleService.list(new QueryWrapper<Role>().in("role_id", roleIds)).stream().map(Role::getRoleName).collect(Collectors.toList());//然后通过身份id再拿到身份和权限的中间表的idList<Integer> moduleIds = roleModuleService.list(new QueryWrapper<RoleModule>().in("role_id", roleIds)).stream().map(RoleModule::getModuleId).collect(Collectors.toList());//再通过这个中间表的id拿到对应的可访问的模块的urlList<String> moduleUrls = moduleService.list(new QueryWrapper<Module>().in("id", moduleIds)).stream().map(Module::getUrl).filter(Objects::nonNull).collect(Collectors.toList());//把roleName和moduleUrls整合在一起roleName.addAll(moduleUrls);//把拿到的角色的名称以及角色可访问到的url赋值给user中的哪个管理角色权限的字段// roles [管理员,普通用户,book:manager:add,book:manager:list]List<SimpleGrantedAuthority> authorities = roleName.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());user.setAuthorities(authorities);return user;}
}

2.修改SpringSecurity配置类

加一个注解

@EnableGlobalMethodSecurity(prePostEnabled = true)

@EnableGlobalMethodSecurity是Spring Security提供的一个注解,用于启用方法级别的安全性。它可以在任何@Configuration类上使用,以启用Spring Security的方法级别的安全性功能。它接受一个或多个参数,用于指定要使用的安全注解类型和其他选项。以下是一些常用的参数:

  • prePostEnabled:如果设置为true,则启用@PreAuthorize@PostAuthorize注解。默认值为false

  • securedEnabled:如果设置为true,则启用@Secured注解。默认值为false

  • jsr250Enabled:如果设置为true,则启用@RolesAllowed注解。默认值为false

  • proxyTargetClass:如果设置为true,则使用CGLIB代理而不是标准的JDK动态代理。默认值为false

使用@EnableGlobalMethodSecurity注解后,可以在应用程序中使用Spring Security提供的各种注解来保护方法,例如@Secured@PreAuthorize@PostAuthorize@RolesAllowed。这些注解允许您在方法级别上定义安全规则,以控制哪些用户可以访问哪些方法。

注解介绍:

注解说明
@PreAuthorize用于在方法执行之前对访问进行权限验证
@PostAuthorize用于在方法执行之后对返回结果进行权限验证
@Secured用于在方法执行之前对访问进行权限验证
@RolesAllowed是Java标准的注解之一,用于在方法执行之前对访问进行权限验证

3.测试

使用管理员角色登录

使用普通角色登录

如果说没有权限或者是退出登录等其他操作,都会是跳到指定的页面

三.基于前后端分离项目的授权 

关于前面的配置都是和上面所说的一样,这里就不过多阐述了,但是前后端分离的项目就是不能够直接跳转页面了,而是要返回响应的内容。所以我们需要写一个专门的响应类

JsonResponseBody

package com.wh.security.resp;import lombok.Data;@Data
public class JsonResponseBody<T> {private Integer code;private String msg;private T data;private Long total;private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data) {this.code = jsonResponseStatus.getCode();this.msg = jsonResponseStatus.getMsg();this.data = data;}private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data, Long total) {this.code = jsonResponseStatus.getCode();this.msg = jsonResponseStatus.getMsg();this.data = data;this.total = total;}public static <T> JsonResponseBody<T> success() {return new JsonResponseBody<T>(JsonResponseStatus.OK, null);}public static <T> JsonResponseBody<T> success(T data) {return new JsonResponseBody<T>(JsonResponseStatus.OK, data);}public static <T> JsonResponseBody<T> success(T data, Long total) {return new JsonResponseBody<T>(JsonResponseStatus.OK, data, total);}public static <T> JsonResponseBody<T> unknown() {return new JsonResponseBody<T>(JsonResponseStatus.UN_KNOWN, null);}public static <T> JsonResponseBody<T> other(JsonResponseStatus jsonResponseStatus) {return new JsonResponseBody<T>(jsonResponseStatus, null);}}

JsonResponseStatus

package com.wh.security.resp;import lombok.Getter;@Getter
public enum JsonResponseStatus {OK(200, "OK"),UN_KNOWN(500, "未知错误"),RESULT_EMPTY(1000, "查询结果为空"),NO_ACCESS(3001, "没有权限"),NO_LOGIN(4001, "没有登录"),LOGIN_FAILURE(5001, "登录失败"),;private final Integer code;private final String msg;JsonResponseStatus(Integer code, String msg) {this.code = code;this.msg = msg;}}

并且需要在配置类中将登录成功或者权限认证失败等这些需要跳转页面的操作全部都修改掉

修改SpringSecurity配置类

package com.wh.security.config;import com.fasterxml.jackson.databind.ObjectMapper;
import com.wh.security.resp.JsonResponseBody;
import com.wh.security.resp.JsonResponseStatus;
import com.wh.security.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import javax.annotation.Resource;
import javax.sql.DataSource;@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {// 自动装配自定义的用户服务实现类@Autowiredprivate MyUserDetailsService myUserDetailsService;// 自动装配数据源@Resourcepublic DataSource dataSource;// 自动装配对象映射器@Autowiredprivate ObjectMapper objectMapper;// 自动装配自定义的登录失败处理器@Autowiredprivate MyAuthenticationFailureHandler myAuthenticationFailureHandler;// 创建密码编码器实例@Beanpublic PasswordEncoder bcryptPasswordEncoder() {return new BCryptPasswordEncoder();}// 创建模拟用户数据的服务实现类// @Autowired// private UserDetailsService userDetailsService;// 获取认证管理器(认证管理器),登录时认证使用(基于数据库方式)@Beanpublic AuthenticationManager authenticationManager() throws Exception {// 创建DaoAuthenticationProvider实例DaoAuthenticationProvider provider=new DaoAuthenticationProvider();// 设置userDetailsService,基于数据库方式进行身份认证provider.setUserDetailsService(myUserDetailsService);// 配置密码编码器provider.setPasswordEncoder(bcryptPasswordEncoder());return new ProviderManager(provider);}// 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置@Beanpublic PersistentTokenRepository persistentTokenRepository(){JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);// 设置为true要保障数据库该表不存在,不然会报异常哦// 所以第二次打开服务器应用程序的时候得把它设为falsetokenRepository.setCreateTableOnStartup(false);return tokenRepository;}// 配置安全过滤器链(SecurityFilterChain)@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/", "/noAccess", "/toLogin").permitAll()//所有请求全部需要登录.anyRequest().authenticated().and().formLogin().loginPage("/toLogin")//设置处理登录请求的接口.loginProcessingUrl("/userLogin").usernameParameter("username").passwordParameter("password")//登录成功.successHandler((req, resp, auth) -> {Object user = auth.getPrincipal();objectMapper.writeValue(resp.getOutputStream(), JsonResponseBody.success(user));})//登录失败.failureHandler(myAuthenticationFailureHandler).and().exceptionHandling()//权限不足.accessDeniedHandler((req, resp, ex) -> {objectMapper.writeValue(resp.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.NO_ACCESS));})//没有认证.authenticationEntryPoint((req, resp, ex) -> {objectMapper.writeValue(resp.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.NO_LOGIN));}).and().logout().logoutUrl("/logout").logoutSuccessUrl("/");http.csrf().disable(); //禁用CSRF保护return http.build();}
}

这样就不会跳页面了,而是返回出对应的数据

好啦,今天的分享就到这了,希望能够帮到你呢!😊😊   

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

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

相关文章

Netty-2-数据编解码

解析编解码支持的原理 以编码为例&#xff0c;要将对象序列化成字节流&#xff0c;你可以使用MessageToByteEncoder或MessageToMessageEncoder类。 这两个类都继承自ChannelOutboundHandlerAdapter适配器类&#xff0c;用于进行数据的转换。 其中&#xff0c;对于MessageToMe…

基于 Webpack 插件体系的 Mock 服务

背景 在软件研发流程中&#xff0c;对于前后端分离的架构体系而言&#xff0c;为了能够更快速、高效的实现功能的开发&#xff0c;研发团队通常来说会在产品原型阶段对前后端联调的数据接口进行结构设计及约定&#xff0c;进而可以分别同步进行对应功能的实现&#xff0c;提升研…

深度学习 | 基础卷积神经网络

卷积神经网络是人脸识别、自动驾驶汽车等大多数计算机视觉应用的支柱。可以认为是一种特殊的神经网络架构&#xff0c;其中基本的矩阵乘法运算被卷积运算取代&#xff0c;专门处理具有网格状拓扑结构的数据。 1、全连接层的问题 1.1、全连接层的问题 “全连接层”的特点是每个…

kubernetes集群 应用实践 kafka部署

kubernetes集群 应用实践 kafka部署 零.1、环境说明 零.2、kafka架构说明 zookeeper在kafka集群中的作用 一、Broker注册 二、Topic注册 三、Topic Partition选主 四、生产者负载均衡 五、消费者负载均衡 一、持久化存储资源准备 1.1 创建共享目录 [rootnfsserver ~]# mkdir -…

锯齿云服务器租赁使用教程

首先登陆锯齿云账号 网盘上传数据集与代码 随后我们需要做的是将所需要的数据集与代码上传到网盘&#xff08;也可以直接在租用服务器后将数据集与代码传到服务器的硬盘上&#xff0c;但这样做会消耗大量时间&#xff0c;造成资源浪费&#xff09; 点击工作空间&#xff1a;…

谷粒商城-商品服务-新增商品功能开发(商品图片无法展示问题没有解决)

在网关配置路由 - id: member_routeuri: lb://gulimemberpredicates:- Path/api/gulimember/**filters:- RewritePath/api/(?<segment>.*),/$\{segment}并将所有逆向生成的工程调式出来 获取分类关联的品牌 例如&#xff1a;手机&#xff08;分类&#xff09;-> 品…

Python算法例26 落单的数Ⅳ

1. 问题描述 给定数组&#xff0c;除了一个数出现一次外&#xff0c;所有数都出现两次&#xff0c;并且所有出现两次的数都挨着&#xff0c;找出出现一次的数。 2. 问题示例 给出nums[3&#xff0c;3&#xff0c;2&#xff0c;2&#xff0c;4&#xff0c;5&#xff0c;5]&am…

ZooKeeper 使用介绍和原理详解

目录 1. 介绍 重要性 应用场景 2. ZooKeeper 架构 服务角色 数据模型 工作原理 3. 安装和配置 下载 ZooKeeper 安装和配置 启动 ZooKeeper 验证和管理 停止和关闭 4. ZooKeeper 数据模型 数据结构和层次命名空间&#xff1a; 节点类型和 Watcher 机制&#xff…

基于python的excel检查和读写软件

软件版本&#xff1a;python3.6 窗口和界面gui代码&#xff1a; class mygui:def _init_(self):passdef run(self):root Tkinter.Tk()root.title(ExcelRun)max_w, max_h root.maxsize()root.geometry(f500x500{int((max_w - 500) / 2)}{int((max_h - 300) / 2)}) # 居中显示…

【MySQL】MySQL的数据类型

MySQL的数据类型 一、数据类型分类二、数值类型1、整数类型2、bit类型3、小数类型 三、字符串类型四、时间日期类型五、enum和set类型enum和set查找 数据类型的作用&#xff1a; 决定了存储数据时应该开辟的空间大小和数据的取值范围。决定了如何识别一个特定的二进制序列。 …

AI创作系统ChatGPT系统源码,支持Midjourney绘画,GPT语音对话+DALL-E3文生图

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

R语言基础 | 安徽某高校《统计建模与R软件》期末复习

第一节 数字、字符与向量 1.1 向量的赋值 c<-(1,2,3,4,5) 1.2 向量的运算 对于向量&#xff0c;我们可以直接对其作加&#xff08;&#xff09;&#xff0c;减&#xff08;-&#xff09;&#xff0c;乘&#xff08;*&#xff09;&#xff0c;除&#xff08;/&#xff09…

【shell脚本实战学习笔记】#1

shell脚本实战学习笔记#1 脚本编写场景需求&#xff1a; 编写一个比较数据大小的shell脚本&#xff0c;要求判断用户只能输入两位数字&#xff0c;不能是字符或其他特殊字符&#xff1b;并且在shell脚本中需要用到函数来控制执行顺序。 知识点&#xff1a;shell函数&#xff…

科研学习|论文解读——面向电商内容安全风险管控的协同过滤推荐算法研究

【论文完整内容详见知网链接】&#xff1a; 面向电商内容安全风险管控的协同过滤推荐算法研究 - 中国知网 (cnki.net) 面向电商内容安全风险管控的协同过滤推荐算法研究* 摘 要&#xff1a;[目的/意义]随着电商平台商家入驻要求降低以及商品上线审核流程简化&#xff0c;内容安…

Centos安装vsftpd:centos配置vsftpd,ftp报200和227错误

一、centos下载安装vsftpd&#xff08;root权限&#xff09; 1、下载安装 yum -y install vsftpd 2、vsftpd的配置文件 /etc/vsftpd.conf 3、备份原来的配置文件 sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.backup 4、修改配置文件如下&#xff1a;vi /etc/vsftpd.conf …

体验一下 CodeGPT 插件

体验一下 CodeGPT 插件 0. 背景1. CodeGPT 插件安装2. CodeGPT 插件基本配置3. (可选)CodeGPT 插件预制提示词原始配置(英文)4. CodeGPT 插件预制提示词配置(中文)5. 简单验证一下 0. 背景 看到B站Up主 “wwwzhouhui” 一个关于 CodeGPT 的视频&#xff0c;感觉挺有意思&#…

SpringMVC:整合 SSM 中篇

文章目录 SpringMVC - 04整合 SSM 中篇一、优化二、总结三、说明注意&#xff1a; SpringMVC - 04 整合 SSM 中篇 一、优化 在 spring-dao.xml 中配置 dao 接口扫描&#xff0c;可以动态地实现 dao 接口注入到 Spring 容器中。 优化前&#xff1a;手动创建 SqlSessionTempl…

STM32实现三个小灯亮

led.c #include"led.h"void Led_Init(void) {GPIO_InitTypeDef GPIO_VALUE; //???RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//???GPIO_VALUE.GPIO_ModeGPIO_Mode_Out_PP;//???? ????GPIO_VALUE.GPIO_PinGPIO_Pin_1|GPIO_Pin_2|GPIO_P…

spring-validation实现分组校验

文章目录 前言实际开发可能会使用到分组校验maven添加依赖简单使用高级应用分组自定义分组组合分组 源码地址 前言 JSR 303中提出了Bean Validation&#xff0c;表示JavaBean的校验&#xff0c;Hibernate Validation是其具体实现&#xff0c;并对其进行了一些扩展&#xff0c;…

Arduino上U8g2库显示中文的经历

u8g2自带很多中文库&#xff1b;但是向u8g2_font_wqy12_t_chinese3 比较全的应该是u8g2_font_wqy12_t_gb2312 这个&#xff0c;只是我还没有调用成功 这个库&#xff0c;中文就显示不全&#xff1b;有些没有定义&#xff0c;如百家姓 #include <Arduino.h> #include <…