当前位置: 首页 > 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…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关

在水泥厂的生产流程中&#xff0c;工业自动化网关起着至关重要的作用&#xff0c;尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关&#xff0c;为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多&#xff0c;其中不少设备采用Devicenet协议。Devicen…...

【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道

文/法律实务观察组 在债务重组领域&#xff0c;专业机构的核心价值不仅在于减轻债务数字&#xff0c;更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明&#xff0c;合法债务优化需同步实现三重平衡&#xff1a; 法律刚性&#xff08;债…...

对象回调初步研究

_OBJECT_TYPE结构分析 在介绍什么是对象回调前&#xff0c;首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例&#xff0c;用_OBJECT_TYPE这个结构来解析它&#xff0c;0x80处就是今天要介绍的回调链表&#xff0c;但是先不着急&#xff0c;先把目光…...