免费建站系统怎么用,宜宾 网站建设,网站抓取诊断ip出错,建筑工程教育网官网一、Spring Security
①#xff1a;什么是Spring Security
Spring Security是一个能够为基于Spring的企业应用系统提供声明式#xff08;注解#xff09;的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean#xff0c;充分利用了Spring …一、Spring Security
①什么是Spring Security
Spring Security是一个能够为基于Spring的企业应用系统提供声明式注解的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean充分利用了Spring IoCDI控制反转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 2.添加依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactId
/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional
/dependency
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope
/dependency
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId
/dependency
!--spring security--
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId
/dependency3.创建3个controller 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 会自动跳转到登录页面 框架生成的用户
用户名 user密码 在启动项目时生成的临时密码98d61d12-378d-45ab-97b4-04241651ccd2 2.登录成功 3.登出http://localhost:8080/logout ②自定义用户名和密码 application.yaml中配置如下 spring:security:user:name: rootpassword: root使用刚刚自定义的用户名和密码登录 ③多用户管理基于内存 1.创建配置类 /*** 用户详情服务接口** 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.启动程序使用配置类中的用户登录 3.退出登录使用前面配置文件中的用户登录 结论可以删除配置文件中的用户了
④密码处理加密
前面的用户并没有真正加密 使用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();}
}⑤获取当前登录用户信息 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.启动程序 并登录 3.访问刚刚写的第一个controller的第一个接口 ⑥配置用户权限 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 {OverridePreAuthorize(hasAuthority(teacher:add)) // 预授权 // hasAuthority(teacher:add) 一个权限可访问public String add() {log.info(添加教师成功!);return 添加教师成功!;}OverridePreAuthorize(hasAnyAuthority(teacher:delete)) // hasAnyAuthority(teacher:add,teacher:delete...) 可以有多权限public String delete() {log.info(删除教师成功!);return 删除教师成功!;}OverridePreAuthorize(hasAnyAuthority(teacher:update))public String update() {log.info(修改教师成功!);return 修改教师成功!;}OverridePreAuthorize(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;charsetutf-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;charsetutf-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;charsetutf-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;charsetutf-8);PrintWriter writer response.getWriter();writer.println(responseJson);writer.flush();}});}
}
2.Response
package com.it.vo;import lombok.Data;
Data
public class ResponseT {/*** 结果** 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 ResponseT ok(T t) {return new ResponseT(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.引入依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!--spring security--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.11/version/dependency!--支持使用 JDBC 访问数据库 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jdbc/artifactId/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion2.3.1/version/dependency!-- mybatis-plus-generator --dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-generator/artifactIdversion3.3.2/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.2.16/version/dependency3.配置文件数据库等配置信息 server:port: 8099
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://您的ip地址:3306/security_study?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/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 version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.it.mapper.SysUserMapper!-- 这里定义SQL语句 --select idgetUserName resultTypecom.it.entity.SysUserselect user_id,username,password,sex,address,enabled,account_no_expired,credentials_no_expired,account_no_lockedfrom sys_userwhere username #{userName}/select
/mapper3.实现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 {ListString queryPermissionsByUserId(Param(userId) Integer userId);
}3.创建SysMenuMapper.xml ?xml version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.it.mapper.SysMenuMapper!-- 这里定义SQL语句 --
select idqueryPermissionsByUserId resultTypestringselect 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
/mapper4.创建serviceSysMenuService public interface SysMenuService {ListString queryPermissionsByUserId(Integer userId);
}5.创建SysMenuServiceImpl Service
Slf4j
public class SysMenuServiceImpl implements SysMenuService {Resourceprivate SysMenuMapper sysMenuMapper;Overridepublic ListString queryPermissionsByUserId(Integer userId) {return sysMenuMapper.queryPermissionsByUserId(userId);}
}②: 配置权限 1.修改SecurityUser实体类 加入一个属性
private ListSimpleGrantedAuthority simpleGrantedAuthorities;2.修改方法getAuthorities Overridepublic Collection? extends GrantedAuthority getAuthorities () {// todo 配置权限return simpleGrantedAuthorities;}3.添加一个set方法 public void setSimpleGrantedAuthorities(ListSimpleGrantedAuthority 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获取用户的所有权限ListString permissions sysMenuService.queryPermissionsByUserId(sysUser.getUserId());// 使用Stream流将 ListString permissions 转换为 ListSimpleGrantedAuthority grantedAuthoritiesListSimpleGrantedAuthority 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;}重启服务再次登录查看