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

安全框架SpringSecurity-1(认证入门数据库授权)

一、Spring Security

①:什么是Spring Security

Spring Security是一个能够为基于Spring的企业应用系统提供声明式(注解)的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

可以一句话来概括:SpringSecurity 是一个安全框架

②:官方网址

https://spring.io/projects/spring-security/

中文网址:https://springdoc.cn/spring-security/servlet/authorization/authorize-http-requests.html

二、认证入门

①:安全入门项目

1.新建一个项目01_springsecurity

image.png

2.添加依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring security-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

3.创建3个controller

image.png

@RestController
@RequestMapping("/admin")
public class AdminController {@GetMapping("/query")public String queryInfo(){return "当前登录用户: admin";}
}
@RestController
@RequestMapping("/student")
public class StudentController {@GetMapping("/query")public String queryInfo(){return "当前登录用户: student";}
}
@RestController
@RequestMapping("/teacher")
public class TeacherController {@GetMapping("/query")public String queryInfo(){return "当前登录用户: teacher";}
}

4.启动程序

1.访问 http://localhost:8080/admin/query 会自动跳转到登录页面

image.png

框架生成的用户

  • 用户名: user
  • 密码: 在启动项目时,生成的临时密码(98d61d12-378d-45ab-97b4-04241651ccd2)

image.png

2.登录成功

image.png

3.登出http://localhost:8080/logout

image.png

②:自定义用户名和密码

application.yaml中配置如下

spring:security:user:name: rootpassword: root

使用刚刚自定义的用户名和密码登录

image.png

image.png

③:多用户管理(基于内存)

1.创建配置类

image.png

/*** 用户详情服务接口** @author: Coke* @DateTime: 2023/11/07/20:48**/
@Configuration
public class MySecurityUserConfig {@Beanpublic UserDetailsService userDetailsService () {// 创建两个用户UserDetails zhangsan = User.builder().username("张三").password("123456").roles("student").build();UserDetails lisi = User.builder().username("李四").password("123456").roles("teacher").build();// 创建一个内存用户详细信息管理器InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();// 将两个用户放到 内存用户详细信息管理器中manager.createUser(zhangsan);manager.createUser(lisi);return manager;}/** 自定义用户 必须配置密码编辑器* NoOpPasswordEncoder(没有加密)* @DateTime: 2023/11/7 21:11** @return PasswordEncoder* @author: Coke*/@Beanpublic PasswordEncoder passwordEncoder () {return NoOpPasswordEncoder.getInstance();}
}

2.启动程序(使用配置类中的用户登录)

image.png

image.png

3.退出登录使用前面配置文件中的用户登录

image.png

  • 结论:可以删除配置文件中的用户了

④:密码处理(加密)

  • 前面的用户并没有真正加密

使用BCryptPasswordEncoder进行加密 (重新运行程序测试)

@Configuration
public class MySecurityUserConfig {@Beanpublic UserDetailsService userDetailsService () {// 创建两个用户UserDetails zhangsan = User.builder().username("张三").password(passwordEncoder().encode("123456")).roles("student").build();UserDetails lisi = User.builder().username("李四").password(passwordEncoder().encode("123456")).roles("teacher").build();// 创建一个内存用户详细信息管理器InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();// 将两个用户放到 内存用户详细信息管理器中manager.createUser(zhangsan);manager.createUser(lisi);return manager;}/** 自定义用户 必须配置密码编辑器* NoOpPasswordEncoder(没有加密)* @DateTime: 2023/11/7 21:11** @return PasswordEncoder* @author: Coke*/@Beanpublic PasswordEncoder passwordEncoder () {return new BCryptPasswordEncoder();}
}

image.png

⑤:获取当前登录用户信息

1.创建CurrentLoginUserController

@RestController
@RequestMapping("/getLogin")
public class CurrentLoginUserController {@GetMapping("/user1")public Authentication getUser1(Authentication authentication){return authentication;}@GetMapping("/user2")public Principal getUser2(Principal principal){return principal;}@GetMapping("/user3")public Principal getUser3(){// 通过安全上下文持有器获取安全上下文,再获取认证信息return SecurityContextHolder.getContext().getAuthentication();}
}

2.启动程序 并登录

image.png

3.访问刚刚写的第一个controller的第一个接口
image.png
在这里插入图片描述

⑥:配置用户权限

    @Beanpublic UserDetailsService userDetailsService(){// 创建两个用户UserDetails user1 = User.builder().username("张三").password(passwordEncoder().encode("123456")).roles("student").authorities("student_delete", "student_add").build();UserDetails user2 = User.builder().username("李四").password(passwordEncoder().encode("123456")).authorities("teacher_delete", "teacher_add").roles("teacher").build();// 创建一个内存用户详细信息管理器InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();// 将两个用户放到 内存用户详细信息管理器中manager.createUser(user1);manager.createUser(user2);return manager;}

在这里插入图片描述

1.登录 张三 这个用户 然后查询用户信息

在这里插入图片描述

2.在登录李四 这个账户然后 查询用户信息

在这里插入图片描述

  • 结论:权限和角色按照配置的顺序生效 后者覆盖前者
  • 问题:虽然有了权限 但是并没对访问url生效

⑦:针对url进行授权

上面讲的实现了认证功能,但是受保护的资源是默认的,歌认所有认证(登录)用户均可以访问所有资源瓤不能根据实际情况进行角色管理,要实现授权功能,需重写WebSecurityConfigureAdapter中的一个configure方法

1.新建WebSecurityConfig类,重写configure(HttpSecurity http)方法

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests() // 授权请求// 匹配路径url的写法有三种
//               .regexMatchers("/student/**")
//               .antMatchers("/student/**").mvcMatchers("/student/**") // 推荐这种, 匹配这个url// 判断 权限的五种
//               .hasAuthority( ) // 是否有单个权限
//               .access()
//               .hasRole() // 是否有单个角色
//               .hasAnyRole() // 是否有任意角色.hasAnyAuthority("student_add") // 拥有这个权限的用户可以访问 /student/** 这个url.mvcMatchers("/teacher/**") // 匹配url.hasAuthority("ROLE_teacher") //  拥有这个权限的用户可以访问 /teacher/** 这个url.anyRequest() // 任何请求.authenticated(); // 都需要登录,注意:没有配置mV℃匹配器的只要登录成功就可以访问http.formLogin().permitAll(); // 允许表单登录permit:允许}
}

在这里插入图片描述
在这里插入图片描述

⑧:针对方法进行授权

1.拷贝01_spring_security改名为02_spring_security
删除: AdminController和StudentController
新增: TeacherService、TeacherServiceImpl

在这里插入图片描述

1. TeacherController
@RestController
@RequestMapping("/teacher")
public class TeacherController {@Autowiredprivate TeacherService teacherService;@GetMapping("/add")public String add(){return teacherService.add();}@GetMapping("/delete")public String delete(){return teacherService.delete();}@GetMapping("/update")public String update(){return teacherService.update();}@GetMapping("/query")public String query(){return teacherService.query();}
}
2.TeacherService
public interface TeacherService {// 添加教师String add();// 删除教师String delete();// 修改教师String update();// 查询教师String query();
}
3.TeacherServiceImpl
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {@Overridepublic String add() {log.info("添加教师成功!");return "添加教师成功!";}@Overridepublic String delete() {log.info("删除教师成功!");return "删除教师成功!";}@Overridepublic String update() {log.info("修改教师成功!");return "修改教师成功!";}@Overridepublic String query() {log.info("查询教师成功!");return "查询教师成功!";}
}

2.修改MySecurityUserConfig配置类

@Configuration
public class MySecurityUserConfig {@Beanpublic UserDetailsService userDetailsService(){// 创建两个用户UserDetails user1 = User.builder().username("张三").password(passwordEncoder().encode("123456")).roles("student") // 角色的前面加上 ROLE_ 就成了权限.build();UserDetails user2 = User.builder().username("李四").password(passwordEncoder().encode("123456")).authorities("teacher:query") // 配置了教师的查询权限.build();UserDetails user3 = User.builder().username("admin").password(passwordEncoder().encode("123456")).authorities("teacher:add","teacher:delete","teacher:update","teacher:query") // 配置了教师的增删改查权限.build();// 创建一个内存用户详细信息管理器InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();// 将两个用户放到 内存用户详细信息管理器中manager.createUser(user1);manager.createUser(user2);manager.createUser(user3);return manager;}/** 自定义用户 必须配置密码编辑器* NoOpPasswordEncoder(没有加密)* @DateTime: 2023/11/7 21:11** @return PasswordEncoder* @author: Coke*/@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
}

3.修改WebSecurityConfig配置类

  • 加上启用全局方法安全注解 @EnableGlobalMethodSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启全局方法安全,启用预授权注解和后授权注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated(); // 任何请求 都需要登录,注意:没有配置mV℃匹配器的只要登录成功就可以访问http.formLogin().permitAll(); // 放开登录页面}
}

4.修改TeacherServiceImpl类 在方法上加上预授权注解

@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {@Override@PreAuthorize("hasAuthority('teacher:add')") // 预授权 // hasAuthority('teacher:add') 一个权限可访问public String add() {log.info("添加教师成功!");return "添加教师成功!";}@Override@PreAuthorize("hasAnyAuthority('teacher:delete')") // hasAnyAuthority('teacher:add','teacher:delete'...) 可以有多权限public String delete() {log.info("删除教师成功!");return "删除教师成功!";}@Override@PreAuthorize("hasAnyAuthority('teacher:update')")public String update() {log.info("修改教师成功!");return "修改教师成功!";}@Override@PreAuthorize("hasAnyAuthority('teacher:query')")public String query() {log.info("查询教师成功!");return "查询教师成功!";}
}

5.测试

1.登录用户 张三 没有teacher的任何权限
在这里插入图片描述
2.登录用户 李四 有teacher的查询权限
在这里插入图片描述
3.登录用户 admin 有teacher的所有权限
在这里插入图片描述

⑨:处理返回结果及自定义用户信息

1. 处理返回结果

1.拷贝02_spring_security改名为03_spring_security
新增: WebSecurityConfig、Response

在这里插入图片描述

1.WebSecurityConfig
package com.it.config;import com.fasterxml.jackson.databind.ObjectMapper;
import com.it.vo.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*** @Author: CaoYouGen* @DateTime: 2023/11/08/13:21* @注释: TODO**/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启全局方法安全,启用预授权注解和后授权注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate ObjectMapper objectMapper; // 可以进行序列号(json)和反序列化@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated(); // 任何请求 都需要登录,注意:没有配置mV℃匹配器的只要登录成功就可以访问http.formLogin()// 配置登录成功的处理器.successHandler(new AuthenticationSuccessHandler() {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {String responseJson = objectMapper.writeValueAsString(Response.ok("登录成功!"));response.setCharacterEncoding("utf-8");response.setContentType("application/json;charset=utf-8");PrintWriter writer = response.getWriter();writer.println(responseJson);writer.flush();}})// 配置登录失败的处理器.failureHandler(new AuthenticationFailureHandler() {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {String responseJson = objectMapper.writeValueAsString(Response.error(1,"登录失败!"));response.setCharacterEncoding("utf-8");response.setContentType("application/json;charset=utf-8");PrintWriter writer = response.getWriter();writer.println(responseJson);writer.flush();}}).permitAll();// 配置退出成功处理器http.logout().logoutSuccessHandler(new LogoutSuccessHandler() {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {String responseJson = objectMapper.writeValueAsString(Response.ok("退出成功!"));response.setCharacterEncoding("utf-8");response.setContentType("application/json;charset=utf-8");PrintWriter writer = response.getWriter();writer.println(responseJson);writer.flush();}});// 配置访问拒绝处理器http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {String responseJson = objectMapper.writeValueAsString(Response.error(1,"您没有权限访问该资源!"));response.setCharacterEncoding("utf-8");response.setContentType("application/json;charset=utf-8");PrintWriter writer = response.getWriter();writer.println(responseJson);writer.flush();}});}
}
2.Response
package com.it.vo;import lombok.Data;
@Data
public class Response<T> {/*** 结果** @mock true*/private boolean success;/*** 状态码** @mock 200*/private int code;/*** 消息提示** @mock 操作成功*/private String msg;/*** 结果体** @mock null*/private T data;public Response() {}public Response(int code, Object status) {super();this.code = code;this.msg = status.toString();if (code == 1) {this.success = true;} else {this.success = false;}}public Response(int code, String status, T result) {super();this.code = code;this.msg = status;this.data = result;if (code == 1) {this.success = true;} else {this.success = false;}}public static Response<?> ok() {return new Response<>(1, "success");}public static <T> Response<T> ok(T t) {return new Response<T>(1, "success", t);}public static Response<?> error(String status) {return new Response<>(500, status);}public static Response<?> error(int code, String status) {return new Response<>(code, status);}
}

2.自定义用户信息

1.删除: MySecurityUserConfig
新增: SecurityUser、UserServiceImpl

在这里插入图片描述

1.新增SecurityUser
public class SecurityUser implements UserDetails {// 用户的权限@Overridepublic Collection<? extends GrantedAuthority> getAuthorities () {return null;}@Overridepublic String getPassword () {return new BCryptPasswordEncoder().encode("123456");}@Overridepublic String getUsername () {return "张三";}// 判断帐号是否已经过期@Overridepublic boolean isAccountNonExpired () {return true;}// 判断帐号是否已被锁定@Overridepublic boolean isAccountNonLocked () {return true;}// 判断用户凭证是否已经过期@Overridepublic boolean isCredentialsNonExpired () {return true;}// 是否有效@Overridepublic boolean isEnabled () {return true;}
}
2.新增UserServiceImpl
@Service
public class UserServiceImpl implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername (String username) throws UsernameNotFoundException {// 判断用户名是否为空if (!StringUtils.hasText(username)) {throw new UsernameNotFoundException("用户名不存在!");}// 判断用户是否正确if (!username.equals("张三")) {throw new UsernameNotFoundException("用户名不正确!");}// 执行到这里 说明用户名不为空 并且 用户名正确return new SecurityUser();}
}
3.修改WebSecurityConfig(在该类中新增以下方法)
    @Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}

2.测试

  • 使用 李四 这个用户登录

在这里插入图片描述

  • 使用 张三 这个用户登录

在这里插入图片描述

  • 登录成功后 访问teacher/query 的资源
    在这里插入图片描述
  • 查看张三的权限
    在这里插入图片描述

三、基于数据库认证

①:创建数据库和表

1.创建数据库(security_study

在这里插入图片描述

2.创建表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu`  (`id` int NOT NULL AUTO_INCREMENT COMMENT '编号',`pid` int NULL DEFAULT NULL COMMENT '父级编号',`name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '名称',`code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '权限编码',`type` int NULL DEFAULT NULL COMMENT '0代表菜单1权限2 url',`delete_flag` tinyint NULL DEFAULT 0 COMMENT '0代表未删除,1 代表已删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;-- ----------------------------
-- Records of sys_menu
-- ----------------------------
INSERT INTO `sys_menu` VALUES (1, 0, '学生管理', '/student/**', 0, 0);
INSERT INTO `sys_menu` VALUES (2, 1, '学生查询', 'student:query', 1, 0);
INSERT INTO `sys_menu` VALUES (3, 1, '学生添加', 'student:add', 1, 0);
INSERT INTO `sys_menu` VALUES (4, 1, '学生修改', 'student:update', 1, 0);
INSERT INTO `sys_menu` VALUES (5, 1, '学生删除', 'student:delete', 1, 0);
INSERT INTO `sys_menu` VALUES (6, 1, '导出学生信息', 'student:export', 1, 0);
INSERT INTO `sys_menu` VALUES (7, 0, '教师管理', '/teacher/**', 0, 0);
INSERT INTO `sys_menu` VALUES (9, 7, '教师查询', 'teacher:query', 1, 0);-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (`id` int NOT NULL AUTO_INCREMENT COMMENT '角色ID',`rolename` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '角色名称,英文名称',`remark` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '备注',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, 'ROLE_ADMIN', '管理员');
INSERT INTO `sys_role` VALUES (2, 'ROLE_TEACHER', '老师');
INSERT INTO `sys_role` VALUES (3, 'ROLE_STUDENT', '学生');-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu`  (`rid` int NOT NULL COMMENT '角色表的编号',`mid` int NOT NULL COMMENT '菜单表的编号',PRIMARY KEY (`mid`, `rid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;-- ----------------------------
-- Records of sys_role_menu
-- ----------------------------
INSERT INTO `sys_role_menu` VALUES (1, 1);
INSERT INTO `sys_role_menu` VALUES (3, 1);
INSERT INTO `sys_role_menu` VALUES (2, 2);
INSERT INTO `sys_role_menu` VALUES (3, 2);
INSERT INTO `sys_role_menu` VALUES (1, 3);
INSERT INTO `sys_role_menu` VALUES (2, 3);
INSERT INTO `sys_role_menu` VALUES (1, 4);
INSERT INTO `sys_role_menu` VALUES (2, 4);
INSERT INTO `sys_role_menu` VALUES (1, 5);
INSERT INTO `sys_role_menu` VALUES (2, 5);
INSERT INTO `sys_role_menu` VALUES (3, 6);
INSERT INTO `sys_role_menu` VALUES (1, 9);
INSERT INTO `sys_role_menu` VALUES (2, 9);
INSERT INTO `sys_role_menu` VALUES (3, 9);
INSERT INTO `sys_role_menu` VALUES (1, 10);
INSERT INTO `sys_role_menu` VALUES (1, 17);-- ----------------------------
-- Table structure for sys_role_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_user`;
CREATE TABLE `sys_role_user`  (`uid` int NOT NULL COMMENT '用户编号',`rid` int NOT NULL COMMENT '角色编号',PRIMARY KEY (`uid`, `rid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;-- ----------------------------
-- Records of sys_role_user
-- ----------------------------
INSERT INTO `sys_role_user` VALUES (1, 1);
INSERT INTO `sys_role_user` VALUES (2, 2);
INSERT INTO `sys_role_user` VALUES (3, 3);-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (`user_id` int NOT NULL AUTO_INCREMENT COMMENT '编号',`username` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '登陆名',`password` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '密码',`sex` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '性别',`address` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '地址',`enabled` int NULL DEFAULT 1 COMMENT '是否启动账户0禁用 1启用',`account_no_expired` int NULL DEFAULT 1 COMMENT '账户是否没有过期0已过期 1 正常',`credentials_no_expired` int NULL DEFAULT 1 COMMENT '密码是否没有过期0已过期 1 正常',`account_no_locked` int NULL DEFAULT 1 COMMENT '账户是否没有锁定0已锁定 1 正常',PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'obama', '$2a$10$KyXAnVcsrLaHMWpd3e2xhe6JmzBi.3AgMhteFq8t8kjxmwL8olEDq', '男', '武汉', 1, 1, 1, 1);
INSERT INTO `sys_user` VALUES (2, 'thomas', '$2a$10$KyXAnVcsrLaHMWpd3e2xhe6JmzBi.3AgMhteFq8t8kjxmwL8olEDq', '男', '北京', 1, 1, 1, 1);
INSERT INTO `sys_user` VALUES (3, 'eric', '$2a$10$KyXAnVcsrLaHMWpd3e2xhe6JmzBi.3AgMhteFq8t8kjxmwL8olEDq', '男', '成都', 1, 1, 1, 1);SET FOREIGN_KEY_CHECKS = 1;
  • 执行完以上sql后 一共创建了5张表
    在这里插入图片描述

②:创建新的模块

1. 创建、引入依赖、添加配置

1.创建新的模块(04_spring_security

在这里插入图片描述

2.引入依赖

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--spring security--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.11</version></dependency><!--支持使用 JDBC 访问数据库 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.1</version></dependency><!-- mybatis-plus-generator --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.3.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.16</version></dependency>

3.配置文件(数据库等配置信息)

server:port: 8099
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://您的ip地址:3306/security_study?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: rootpassword: 您的密码mybatis:mapper-locations: classpath:mapper/*.xmlconfiguration:map-underscore-to-camel-case: true # 数据库中下划线 映射到实体类中大小写log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 输出sql语句

2.创建实体类与DAO

1.创建实体类 SysUser

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SysUser implements Serializable {private Integer userId;private String username;private String password;private String sex;private String address;private Integer enabled;private Integer accountNoExpired;private Integer credentialsNoExpired;private Integer accountNoLocked;
}

2.创建MapperSysUseMapper

@Mapper
public interface SysUserMapper {/** 根据用户名获取用户信息* @DateTime: 2023/11/8 21:40** @param userName:* @return SysUser* @author: Coke*/SysUser getUserName (@Param ("userName") String userName);
}

3.创建SysUserMapper.xml
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.it.mapper.SysUserMapper"><!-- 这里定义SQL语句 --><select id="getUserName" resultType="com.it.entity.SysUser">select user_id,username,password,sex,address,enabled,account_no_expired,credentials_no_expired,account_no_lockedfrom sys_userwhere username = #{userName}</select>
</mapper>

3.实现Service层

1.创建SysUserService

public interface SysUserService {/** 根据用户名获取用户信息* @DateTime: 2023/11/8 21:40** @param userName:* @return SysUser* @author: Coke*/SysUser getUserName (String userName);
}

2.创建实现了SysUserServiceImpl

@Service
@Slf4j
public class SysUserServiceImpl implements SysUserService {@Autowiredprivate SysUserMapper sysUserMapper;@Overridepublic SysUser getUserName (String userName) {return sysUserMapper.getUserName(userName);}
}

4. 创建安全用户与实现

1.创建SecurityUser
在这里插入图片描述

public class SecurityUser implements UserDetails {private final SysUser sysUser;public SecurityUser (SysUser sysUser) {this.sysUser = sysUser;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities () {// todo 还没有配置权限return null;}@Overridepublic String getPassword () {return this.sysUser.getPassword();}@Overridepublic String getUsername () {return this.sysUser.getUsername();}@Overridepublic boolean isAccountNonExpired () {return this.sysUser.getAccountNoExpired().equals(1);}@Overridepublic boolean isAccountNonLocked () {return this.sysUser.getAccountNoLocked().equals(1);}@Overridepublic boolean isCredentialsNonExpired () {return this.sysUser.getCredentialsNoExpired().equals(1);}@Overridepublic boolean isEnabled () {return this.sysUser.getEnabled().equals(1);}
}

2.创建SecurityUserDetailsServiceImpl

在这里插入图片描述

@Service
public class SecurityUserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate SysUserService sysUserService;@Overridepublic UserDetails loadUserByUsername (String username) throws UsernameNotFoundException {SysUser sysUser = sysUserService.getUserName(username);// 判断对象是否为空if (ObjectUtils.isEmpty(sysUser)){throw new UsernameNotFoundException("该用户不存在!");}// 判断用户是否可用if (!sysUser.getAccountNoExpired().equals(1)) {throw new UsernameNotFoundException("该账户已过期!");}return new SecurityUser(sysUser);}
}

5. 创建安全配置类与Controller层

1.创建安全配置类``

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {// 对密码进行编码@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}@Overrideprotected void configure (HttpSecurity http) throws Exception {// 任何请求 都需要登录,注意:没有配置mV℃匹配器的只要登录成功就可以访问http.authorizeRequests().anyRequest().authenticated();http.formLogin().permitAll();}
}

2.新建三个ControllerStudentController TeacherController CurrentLoginUserController

  • StudentController
@RestController
@Slf4j
@RequestMapping ("/student")
public class StudentController {@GetMapping ("/query")public String queryInfo(){return "query student";}@GetMapping("/add")public String addInfo(){return "add  student!";}@GetMapping("/update")public String updateInfo(){return "update student";}@GetMapping("/delete")public String deleteInfo(){return "delete  student!";}@GetMapping("/export")public String exportInfo(){return "export  student!";}
}
  • TeacherController
@RestController
@Slf4j
@RequestMapping ("/teacher")
public class TeacherController {@GetMapping ("/query")@PreAuthorize ("hasAuthority('teacher:query')")public String queryInfo(){return "I am a teacher!";}
}
  • CurrentLoginUserController
@RestController
@RequestMapping("/getLogin")
public class CurrentLoginUserController {@GetMapping("/user1")public Authentication getUser1(Authentication authentication) {return authentication;}@GetMapping("/user2")public Principal getUser2(Principal principal){return principal;}@GetMapping("/user3")public Principal getUser3(){// 通过安全上下文持有器获取安全上下文,再获取认证信息return SecurityContextHolder.getContext().getAuthentication();}
}

6.启动测试

1.使用thomas和obama分别登录测试,发现student/query等能访问,teacher/query 不能访问

在这里插入图片描述

2.原因:发现用户没有权限,但是/teacher/query 需要访问权限

在这里插入图片描述

四、基于数据库的授权

①:创建实体类、Mapper、service

1.创建菜单(权限)实体类SysMenu

@Data
public class SysMenu implements Serializable {private Integer id;private Integer pid;private Integer type;private String name;private String code;
}

2.创建mapperSysMenuMapper

public interface SysMenuMapper {List<String> queryPermissionsByUserId(@Param("userId") Integer userId);
}

3.创建SysMenuMapper.xml

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.it.mapper.SysMenuMapper"><!-- 这里定义SQL语句 -->
<select id="queryPermissionsByUserId" resultType="string">select distinct sm.codefrom sys_role_user srujoin sys_role_menu srm on sru.rid = srm.ridjoin sys_menu sm on srm.mid = sm.id where sru.rid = #{userId};
</select>
</mapper>

4.创建serviceSysMenuService

public interface SysMenuService {List<String> queryPermissionsByUserId(Integer userId);
}

5.创建SysMenuServiceImpl

@Service
@Slf4j
public class SysMenuServiceImpl implements SysMenuService {@Resourceprivate SysMenuMapper sysMenuMapper;@Overridepublic List<String> queryPermissionsByUserId(Integer userId) {return sysMenuMapper.queryPermissionsByUserId(userId);}
}

②: 配置权限

1.修改SecurityUser实体类

  • 加入一个属性
private List<SimpleGrantedAuthority> simpleGrantedAuthorities;

2.修改方法getAuthorities

    @Overridepublic Collection<? extends GrantedAuthority> getAuthorities () {// todo 配置权限return simpleGrantedAuthorities;}

3.添加一个set方法

public void setSimpleGrantedAuthorities(List<SimpleGrantedAuthority> simpleGrantedAuthorities) {this.simpleGrantedAuthorities = simpleGrantedAuthorities;}

在这里插入图片描述

4.修改SecurityUserDetailsServiceImpl

  • 增加设置权限的步骤
@Service
public class SecurityUserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate SysUserService sysUserService;@Autowiredprivate SysMenuService sysMenuService;@Overridepublic UserDetails loadUserByUsername (String username) throws UsernameNotFoundException {SysUser sysUser = sysUserService.getUserName(username);// 判断对象是否为空if (ObjectUtils.isEmpty(sysUser)){throw new UsernameNotFoundException("该用户不存在!");}// 判断用户是否可用if (!sysUser.getAccountNoExpired().equals(1)) {throw new UsernameNotFoundException("该账户已过期!");}// 通过用户id获取用户的所有权限List<String> permissions = sysMenuService.queryPermissionsByUserId(sysUser.getUserId());// 使用Stream流将 List<String> permissions 转换为 List<SimpleGrantedAuthority> grantedAuthoritiesList<SimpleGrantedAuthority> grantedAuthorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());// 创建一个用户权限对象SecurityUser securityUser = new SecurityUser(sysUser);// 将权限设置到用户对象中securityUser.setSimpleGrantedAuthorities(grantedAuthorities);// 返回return securityUser;}
}

③:启动程序测试

  • 使用thomas和obama分别登录测试,发现已经有权限功能了
在这里插入图片描述 在这里插入图片描述

④:手动擦除密码防止传到前端

  • 擦除密码前
    在这里插入图片描述

1. 修改SecurityUser类中的getPassword方法

    @Overridepublic String getPassword () {String myPassword = this.sysUser.getPassword();// 手动擦除密码防止传到前端this.sysUser.setPassword(null);return myPassword;}

在这里插入图片描述

  • 重启服务再次登录查看

在这里插入图片描述

相关文章:

安全框架SpringSecurity-1(认证入门数据库授权)

一、Spring Security ①&#xff1a;什么是Spring Security Spring Security是一个能够为基于Spring的企业应用系统提供声明式&#xff08;注解&#xff09;的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean&#xff0c;充分利用了Spring …...

【MybatisPlus】条件构造器、自定义SQL、Service接口

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 MybatisPlus 一、条件构造器1.1 基于QueryW…...

数组计算广播

Numpy数组不需要循环遍历&#xff0c;即可对每个元素执行批量的算术运算操作&#xff08;矢量化运算&#xff09;。当两个数组大小&#xff08;Numpy.shape&#xff09;不同时&#xff0c;进行算术运算会出现广播机制。 数组广播 数组在进行矢量化运算的时&#xff0c;要求数组…...

代码解读:Zero-shot 视频生成任务 Text2Video-Zero

Diffusion Models视频生成-博客汇总 前言:上一篇博客《【ICCV 2023 Oral】解读Text2Video-Zero:解锁 Zero-shot 视频生成任务》解读了这篇论文《Text2Video-Zero: Text-to-Image Diffusion Models are Zero-Shot Video Generators》。这篇论文的创新点比较多,含金量很高,而…...

hub.docker访问不了的问题(一步解决)

暂时我也不清楚&#xff0c;但是下面这个网址可以用(可以先用着)Docker Hub Container Image Library | App Containerization (axlinux.top)https://hub.axlinux.top/...

[.NET] Speex 语音编解码介绍, 使用, 代码示例

Speex 是一个开源的, 适合语音编解码的算法, 常应用于网络电话中. 在下面的的介绍中, 我们将使用 SpeexSharp 对 Speex 编码在 .NET 中的使用做介绍 SpeexSharp 可以在 nuget 中直接安装, 并且已经封装了编解码器的类供使用. 如果你不希望了解 Speex 的具体编解码过程, 可以忽…...

小样本目标检测(Few-Shot Object Detection)综述

背景 前言:我的未来研究方向就是这个,所以会更新一系列的文章,就关于FSOD,如果有相同研究方向的同学欢迎沟通交流,我目前研一,希望能在研一发文,目前也有一些想法,但是具体能不能实现还要在做的过程中慢慢评估和实现.写文的主要目的还是记录,避免重复劳动,我想用尽量简洁的语言…...

【解决问题】---- 解决 avue-crud 表格勾选数据翻页后界面保持选中

1. 错误预览 第一页选择【7、8、9、10】 直接点击第三页未进行选择 直接点击第四页未进行选择 2. 问题总结 通过测试可以看到&#xff0c;页面的选择项会影响到其他页面的选择&#xff1b;点击保存&#xff0c;返回的数据却是真真选择的数据&#xff1b;数据在选择渲染…...

JL-03小型气象站气象环境在线监测设备自动上传并保存数据

JL-03小型气象站产品概述 小型气象站用于对风速、风向、雨量、空气温度、空气湿度、太阳辐射、光照强度、土壤温度、土壤湿度、蒸发量、大气压力等气象要素进行现场监测。既可以通过无线通讯将数据传送至云平台&#xff0c;又可以通过配套的数据采集通讯线与计算机进行连接&am…...

Ansible的变量(vars,register,set_fact)

环境 控制节点&#xff1a;Ubuntu 22.04Ansible 2.10.8管理节点&#xff1a;CentOS 8 概述 vars &#xff1a;Ansible关键字&#xff0c;用在play、role、block、task上register &#xff1a;Ansible关键字&#xff0c;用在task上。注意它是一个返回值&#xff0c;可能需要用…...

麒麟KYLINIOS软件仓库搭建03-软件仓库添加新版本的软件包

原文链接&#xff1a;麒麟KYLINIOS软件仓库搭建03-软件仓库添加新版本的软件包 hello&#xff0c;大家好啊&#xff0c;今天给大家带来麒麟桌面操作系统软件仓库搭建的文章03-软件仓库添加新版本的软件包&#xff0c;本篇文章主要给大家介绍了如何在麒麟桌面操作系统2203-x86版…...

监控系统是怎么组的(sentry)

搭建sentry监控平台&#xff0c;实现前后端异常监控。——从零开始搭建一个高颜值后台管理系统全栈框架(十六) - 掘金...

Java --- 直接内存

一、直接内存 1、不是虚拟机运行时数据区的一部分&#xff0c;也不是《Java虚拟机规范》中定义的内存区域。 2、直接内存是在Java堆外的&#xff0c;直接向系统申请的内存区间。 3、来源于NIO&#xff0c;通过存在堆中的DirectByteBuffer操作Native内存。 4、访问直接内存的…...

数据结构与算法之排序: Leetcode 21. 合并两个有序链表 (Typescript版)

合并两个有序链表 https://leetcode.cn/problems/merge-two-sorted-lists/ 描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 …...

AIGC:使用bert_vits2实现栩栩如生的个性化语音克隆

1 VITS2模型 1.1 摘要 单阶段文本到语音模型最近被积极研究&#xff0c;其结果优于两阶段管道系统。以往的单阶段模型虽然取得了较大的进展&#xff0c;但在间歇性非自然性、计算效率、对音素转换依赖性强等方面仍有改进的空间。本文提出VITS2&#xff0c;一种单阶段的文本到…...

2023年CKA考试真题及注意事项

2023年CKA考试真题及注意事项 注意事项考试题目原题解析1.RBAC2.节点维护3.K8S组件升级 1.28.0升级到1.28.14.Etcd备份与恢复5.NetworkPolicy6.Service7.Ingress8.指定节点部署9.检查Node节点健康状态10.一个Pod多个容器11.监控Pod度量指标12.监控Pod日志13.PersistentVolumeCl…...

云计算运维面试

一、Linux的启动过程 1.加电 2.加载bios设置 3.加载grub 4. 加载内核系统到内存中 5.加载配置文件 6.加载内核模块 7.完成相应初始化工作和启动相应服务 8.启动系统进程 9.出现登录界面 10.开机自启动完成 二、查看系统的版本和内核 1、 查看版本 cat /etc/redha…...

Qt实现TCP调试助手 - 简述如何在Qt中实现TCP多并发

简介 软件开发中&#xff0c;可能经常会用到TCP调试工具。本人使用QT开发了一款TCP调试工具&#xff0c;方便大家使用。本文章主要介绍下&#xff0c;该工具的功能&#xff0c;以及如何在Qt中实现TCP服务器的并发。 界面展示 安装界面 桌面图标。安装后会生成桌面图标&#…...

【Python OpenCV】OpenCV介绍

文章目录 前言一、OpenCV简介二、基本功能三、实际应用场景四、Python安装OpenCV总结 前言 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和图像处理库&#xff0c;它提供了丰富的工具和函数&#xff0c;用于处理图像和视频。由于…...

11-09 周四 CNN 卷积神经网络基础知识

11-09 周四 CNN 卷积神经网络 时间版本修改人描述2023年11月9日09:38:12V0.1宋全恒新建文档 简介 学习一下CNN&#xff0c;卷积神经网络。使用的视频课程。视觉相关的任务&#xff1a; 人脸识别 卷积网络与传统网络的区别&#xff1a; <img altimage-20231109094400591 s…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...