🎉 step-3-前后端分离

This commit is contained in:
bunny 2025-07-16 20:44:00 +08:00
parent 075925687c
commit a317acdb63
146 changed files with 25879 additions and 71 deletions

View File

@ -21,6 +21,7 @@
<module>official</module>
<module>step-1</module>
<module>step-2</module>
<module>step-3</module>
</modules>
<properties>

View File

@ -0,0 +1,38 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mall</groupId>
<artifactId>spring-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>step-3</artifactId>
<packaging>jar</packaging>
<name>step-3</name>
<description>step-3-前后端分离</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
<jwt.version>0.12.6</jwt.version>
</properties>
<dependencies>
<!--jjwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,11 @@
package com.spring.step3;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringSecurityStep3Application {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityStep3Application.class, args);
}
}

View File

@ -0,0 +1,50 @@
package com.spring.step3.config;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Slf4j
public class Knife4jConfig {
@Value("${server.port}")
private String port;
@Bean
public OpenAPI openAPI() {
String url = "http://localhost:" + port;
// 作者等信息
Contact contact = new Contact().name("Bunny").email("1319900154@qq.com").url(url);
// 使用协议
License license = new License().name("MIT").url("https://mit-license.org");
// 相关信息
Info info = new Info().title("Bunny-Admin")
.contact(contact).license(license)
.description("Bunny代码生成器")
.summary("Bunny的代码生成器")
.termsOfService(url)
.version("v1.0.0");
return new OpenAPI().info(info).externalDocs(new ExternalDocumentation());
}
@Bean
public GroupedOpenApi all() {
return GroupedOpenApi.builder().group("全部请求接口").pathsToMatch("/api/**").build();
}
@Bean
public GroupedOpenApi security() {
return GroupedOpenApi.builder().group("security接口").pathsToMatch("/api/security/**").build();
}
}

View File

@ -0,0 +1,45 @@
package com.spring.step3.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.spring.step3.config.context.BaseContext;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 配置MP在修改和新增时的操作
*/
@Component
public class MyBatisPlusFieldConfig implements MetaObjectHandler {
/**
* 使用mp做添加操作时候这个方法执行
*/
@Override
public void insertFill(MetaObject metaObject) {
// 设置属性值
this.strictInsertFill(metaObject, "isDeleted", Integer.class, 0);
this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
if (BaseContext.getUsername() != null) {
this.setFieldValByName("createUser", BaseContext.getUserId(), metaObject);
this.setFieldValByName("updateUser", BaseContext.getUserId(), metaObject);
} else {
this.setFieldValByName("createUser", 0L, metaObject);
this.setFieldValByName("updateUser", BaseContext.getUserId(), metaObject);
}
}
/**
* 使用mp做修改操作时候这个方法执行
*/
@Override
public void updateFill(MetaObject metaObject) {
if (BaseContext.getUserId() != null) {
this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
this.setFieldValByName("updateUser", BaseContext.getUserId(), metaObject);
}
}
}

View File

@ -0,0 +1,35 @@
package com.spring.step3.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 使用分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setDbType(DbType.MYSQL);
paginationInnerInterceptor.setMaxLimit(600L);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
// 乐观锁
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 防止全表删除
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
}

View File

@ -0,0 +1,29 @@
package com.spring.step3.config.context;
public class BaseContext {
private static final ThreadLocal<Long> userId = new ThreadLocal<>();
private static final ThreadLocal<String> username = new ThreadLocal<>();
// 用户id相关
public static Long getUserId() {
return userId.get();
}
public static void setUserId(Long _userId) {
userId.set(_userId);
}
public static String getUsername() {
return username.get();
}
public static void setUsername(String _username) {
username.set(_username);
}
public static void removeUser() {
username.remove();
userId.remove();
}
}

View File

@ -0,0 +1,50 @@
package com.spring.step3.config.web;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import java.io.IOException;
/**
* 去除前端传递的空格
*/
@ControllerAdvice
public class ControllerStringParamTrimConfig {
/**
* 创建 String trim 编辑器
* 构造方法中 boolean 参数含义为如果是空白字符串,是否转换为null
* 即如果为true,那么 " " 会被转换为 null,否者为 ""
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
StringTrimmerEditor propertyEditor = new StringTrimmerEditor(false);
// String 类对象注册编辑器
binder.registerCustomEditor(String.class, propertyEditor);
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return jacksonObjectMapperBuilder -> {
// String 类型自定义反序列化操作
jacksonObjectMapperBuilder
.deserializerByType(String.class, new StdScalarDeserializer<String>(String.class) {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
// // 去除全部空格
// return StringUtils.trimAllWhitespace(jsonParser.getValueAsString());
// 仅去除前后空格
return jsonParser.getValueAsString().trim();
}
});
};
}
}

View File

@ -0,0 +1,23 @@
package com.spring.step3.config.web;
import com.spring.step3.config.context.BaseContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
@Configuration
public class ThreadLocalCleanupInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
return true;
}
@Override
public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) {
BaseContext.removeUser();
}
}

View File

@ -0,0 +1,26 @@
package com.spring.step3.config.web;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final ThreadLocalCleanupInterceptor threadLocalCleanupInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(threadLocalCleanupInterceptor);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@ -1,9 +1,9 @@
package com.spring.step2.controller;
package com.spring.step3.controller;
import com.spring.step2.domain.dto.LoginDto;
import com.spring.step2.domain.vo.LoginVo;
import com.spring.step2.domain.vo.result.Result;
import com.spring.step2.service.user.LoginService;
import com.spring.step3.domain.dto.LoginDto;
import com.spring.step3.domain.vo.LoginVo;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.service.user.LoginService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

View File

@ -0,0 +1,77 @@
package com.spring.step3.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring.step3.domain.dto.permission.PermissionDto;
import com.spring.step3.domain.entity.PermissionEntity;
import com.spring.step3.domain.vo.PermissionVo;
import com.spring.step3.domain.vo.result.PageResult;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.service.roles.PermissionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* 系统权限表 前端控制器
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@Tag(name = "系统权限表", description = "系统权限表相关接口")
@RestController
@RequestMapping(value = "/api/permission")
@RequiredArgsConstructor
public class PermissionController {
private final PermissionService permissionService;
@Operation(summary = "分页查询系统权限表", description = "分页系统权限表")
@GetMapping("{page}/{limit}")
public Result<PageResult<PermissionVo>> getPermissionPage(
@Parameter(name = "page", description = "当前页", required = true)
@PathVariable("page") Integer page,
@Parameter(name = "limit", description = "每页记录数", required = true)
@PathVariable("limit") Integer limit,
PermissionDto dto) {
Page<PermissionEntity> pageParams = new Page<>(page, limit);
PageResult<PermissionVo> pageResult = permissionService.getPermissionPage(pageParams, dto);
return Result.success(pageResult);
}
@Operation(summary = "所有的权限列表", description = "获取所有的权限列表")
@GetMapping("all")
public Result<List<PermissionVo>> getAllPermission() {
List<PermissionVo> voList = permissionService.getAllPermission();
return Result.success(voList);
}
@Operation(summary = "添加系统权限表", description = "添加系统权限表")
@PostMapping()
public Result<String> addPermission(@Valid @RequestBody PermissionDto dto) {
permissionService.addPermission(dto);
return Result.success(ResultCodeEnum.ADD_SUCCESS);
}
@Operation(summary = "更新系统权限表", description = "更新系统权限表")
@PutMapping()
public Result<String> updatePermission(@Valid @RequestBody PermissionDto dto) {
permissionService.updatePermission(dto);
return Result.success(ResultCodeEnum.UPDATE_SUCCESS);
}
@Operation(summary = "删除系统权限表", description = "删除系统权限表")
@DeleteMapping()
public Result<String> deletePermission(@RequestBody List<Long> ids) {
permissionService.deletePermission(ids);
return Result.success(ResultCodeEnum.DELETE_SUCCESS);
}
}

View File

@ -0,0 +1,79 @@
package com.spring.step3.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring.step3.domain.dto.role.RoleDto;
import com.spring.step3.domain.entity.RoleEntity;
import com.spring.step3.domain.vo.RoleVo;
import com.spring.step3.domain.vo.result.PageResult;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.service.roles.RoleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* 系统角色表 前端控制器
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@Tag(name = "系统角色表", description = "系统角色表相关接口")
@RestController
@RequestMapping("/api/role")
@RequiredArgsConstructor
public class RoleController {
private final RoleService roleService;
@Operation(summary = "分页查询系统角色表", description = "分页系统角色表")
@GetMapping("{page}/{limit}")
public Result<PageResult<RoleVo>> getRolePage(
@Parameter(name = "page", description = "当前页", required = true)
@PathVariable("page") Integer page,
@Parameter(name = "limit", description = "每页记录数", required = true)
@PathVariable("limit") Integer limit,
RoleDto dto) {
Page<RoleEntity> pageParams = new Page<>(page, limit);
PageResult<RoleVo> pageResult = roleService.getRolePage(pageParams, dto);
return Result.success(pageResult);
}
@PermitAll
@Operation(summary = "获取全部角色列表", description = "获取全部角色列表")
@GetMapping("all")
public Result<List<RoleVo>> getRoleList() {
List<RoleVo> roleVoList = roleService.getRoleList();
return Result.success(roleVoList);
}
@Operation(summary = "添加系统角色表", description = "添加系统角色表")
@PostMapping()
public Result<String> addRole(@Valid @RequestBody RoleDto dto) {
roleService.addRole(dto);
return Result.success(ResultCodeEnum.ADD_SUCCESS);
}
@Operation(summary = "更新系统角色表", description = "更新系统角色表")
@PutMapping()
public Result<String> updateRole(@Valid @RequestBody RoleDto dto) {
roleService.updateRole(dto);
return Result.success(ResultCodeEnum.UPDATE_SUCCESS);
}
@Operation(summary = "删除系统角色表", description = "删除系统角色表")
@DeleteMapping()
public Result<String> deleteRole(@RequestBody List<Long> ids) {
roleService.deleteRole(ids);
return Result.success(ResultCodeEnum.DELETE_SUCCESS);
}
}

View File

@ -0,0 +1,85 @@
package com.spring.step3.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring.step3.domain.dto.role.AssignRolePermissionDto;
import com.spring.step3.domain.dto.role.RolePermissionDto;
import com.spring.step3.domain.entity.RolePermissionEntity;
import com.spring.step3.domain.vo.RolePermissionVo;
import com.spring.step3.domain.vo.result.PageResult;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.service.roles.RolePermissionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* 角色权限关联表 前端控制器
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@Tag(name = "角色权限关联表", description = "角色权限关联表相关接口")
@RestController
@RequestMapping("/api/role-permission")
@RequiredArgsConstructor
public class RolePermissionController {
private final RolePermissionService rolePermissionService;
@Operation(summary = "分页查询角色权限关联表", description = "分页角色权限关联表")
@GetMapping("{page}/{limit}")
public Result<PageResult<RolePermissionVo>> getRolePermissionPage(
@Parameter(name = "page", description = "当前页", required = true)
@PathVariable("page") Integer page,
@Parameter(name = "limit", description = "每页记录数", required = true)
@PathVariable("limit") Integer limit,
RolePermissionDto dto) {
Page<RolePermissionEntity> pageParams = new Page<>(page, limit);
PageResult<RolePermissionVo> pageResult = rolePermissionService.getRolePermissionPage(pageParams, dto);
return Result.success(pageResult);
}
@GetMapping("permissions")
@Operation(summary = "根据角色id获取权限内容", description = "根据角色id获取权限内容")
public Result<List<RolePermissionVo>> getRolePermissionById(Long permissionId) {
List<RolePermissionVo> voList = rolePermissionService.getRolePermissionById(permissionId);
return Result.success(voList);
}
@Operation(summary = "添加角色权限关联表", description = "添加角色权限关联表")
@PostMapping()
public Result<String> addRolePermission(@Valid @RequestBody RolePermissionDto dto) {
rolePermissionService.addRolePermission(dto);
return Result.success(ResultCodeEnum.ADD_SUCCESS);
}
@Operation(summary = "为角色分配权限", description = "根据角色id分配权限")
@PostMapping("assign-permission")
public Result<String> assignRolePermission(@Valid @RequestBody AssignRolePermissionDto dto) {
rolePermissionService.assignRolePermission(dto);
return Result.success();
}
@Operation(summary = "更新角色权限关联表", description = "更新角色权限关联表")
@PutMapping()
public Result<String> updateRolePermission(@Valid @RequestBody RolePermissionDto dto) {
rolePermissionService.updateRolePermission(dto);
return Result.success(ResultCodeEnum.UPDATE_SUCCESS);
}
@Operation(summary = "删除角色权限关联表", description = "删除角色权限关联表")
@DeleteMapping()
public Result<String> deleteRolePermission(@RequestBody List<Long> ids) {
rolePermissionService.deleteRolePermission(ids);
return Result.success(ResultCodeEnum.DELETE_SUCCESS);
}
}

View File

@ -0,0 +1,69 @@
package com.spring.step3.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring.step3.domain.dto.user.UserDto;
import com.spring.step3.domain.entity.UserEntity;
import com.spring.step3.domain.vo.UserVo;
import com.spring.step3.domain.vo.result.PageResult;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.service.user.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* 用户基本信息表 前端控制器
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@Tag(name = "用户基本信息表", description = "用户基本信息表相关接口")
@RestController
@RequestMapping("/api/user")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@Operation(summary = "分页查询用户基本信息表", description = "分页用户基本信息表")
@GetMapping("{page}/{limit}")
public Result<PageResult<UserVo>> getUserPage(
@Parameter(name = "page", description = "当前页", required = true)
@PathVariable("page") Integer page,
@Parameter(name = "limit", description = "每页记录数", required = true)
@PathVariable("limit") Integer limit,
UserDto dto) {
Page<UserEntity> pageParams = new Page<>(page, limit);
PageResult<UserVo> pageResult = userService.getUserPage(pageParams, dto);
return Result.success(pageResult, ResultCodeEnum.LOAD_FINISHED);
}
@Operation(summary = "添加用户基本信息表", description = "添加用户基本信息表")
@PostMapping()
public Result<String> addUser(@Valid @RequestBody UserDto dto) {
userService.addUser(dto);
return Result.success(ResultCodeEnum.ADD_SUCCESS);
}
@Operation(summary = "更新用户基本信息表", description = "更新用户基本信息表")
@PutMapping()
public Result<String> updateUser(@Valid @RequestBody UserDto dto) {
userService.updateUser(dto);
return Result.success(ResultCodeEnum.UPDATE_SUCCESS);
}
@Operation(summary = "删除用户基本信息表", description = "删除用户基本信息表")
@DeleteMapping()
public Result<String> deleteUser(@RequestBody List<Long> ids) {
userService.deleteUser(ids);
return Result.success(ResultCodeEnum.DELETE_SUCCESS);
}
}

View File

@ -0,0 +1,84 @@
package com.spring.step3.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring.step3.domain.dto.user.AssignUserRoleDto;
import com.spring.step3.domain.dto.user.UserRoleDto;
import com.spring.step3.domain.entity.UserRoleEntity;
import com.spring.step3.domain.vo.UserRoleVo;
import com.spring.step3.domain.vo.result.PageResult;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.service.user.UserRoleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* 用户角色关联表 前端控制器
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@Tag(name = "用户角色关联表", description = "用户角色关联表相关接口")
@RestController
@RequestMapping("/api/user-role")
@RequiredArgsConstructor
public class UserRoleController {
private final UserRoleService userRoleService;
@Operation(summary = "分页查询用户角色关联表", description = "分页用户角色关联表")
@GetMapping("{page}/{limit}")
public Result<PageResult<UserRoleVo>> getUserRolePage(
@Parameter(name = "page", description = "当前页", required = true)
@PathVariable("page") Integer page,
@Parameter(name = "limit", description = "每页记录数", required = true)
@PathVariable("limit") Integer limit,
UserRoleDto dto) {
Page<UserRoleEntity> pageParams = new Page<>(page, limit);
PageResult<UserRoleVo> pageResult = userRoleService.getUserRolePage(pageParams, dto);
return Result.success(pageResult);
}
@Operation(summary = "根据用户id获取当前用户角色列表", description = "根据用户id获取当前用户角色列表")
@GetMapping("roles")
public Result<List<UserRoleVo>> getRoleListByUserId(Long userId) {
List<UserRoleVo> voList = userRoleService.getRoleListByUserId(userId);
return Result.success(voList);
}
@Operation(summary = "添加用户角色关联表", description = "添加用户角色关联表")
@PostMapping()
public Result<String> addUserRole(@Valid @RequestBody UserRoleDto dto) {
userRoleService.addUserRole(dto);
return Result.success(ResultCodeEnum.ADD_SUCCESS);
}
@Operation(summary = "为用户分配角色id", description = "根据用户id分配用户角色")
@PostMapping("assign-role")
public Result<String> assignUserRole(@Valid @RequestBody AssignUserRoleDto dto) {
userRoleService.assignUserRole(dto);
return Result.success();
}
@Operation(summary = "更新用户角色关联表", description = "更新用户角色关联表")
@PutMapping()
public Result<String> updateUserRole(@Valid @RequestBody UserRoleDto dto) {
userRoleService.updateUserRole(dto);
return Result.success(ResultCodeEnum.UPDATE_SUCCESS);
}
@Operation(summary = "删除用户角色关联表", description = "删除用户角色关联表")
@DeleteMapping()
public Result<String> deleteUserRole(@RequestBody List<Long> ids) {
userRoleService.deleteUserRole(ids);
return Result.success(ResultCodeEnum.DELETE_SUCCESS);
}
}

View File

@ -0,0 +1,33 @@
package com.spring.step3.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class WebController {
@GetMapping("")
public String indexPage() {
return "index";
}
@GetMapping("/login")
public String showLoginPage() {
return "loginPage";
}
@GetMapping("/user")
public String showUserPage() {
return "userPage";
}
@GetMapping("/role")
public String showRolePage() {
return "rolePage";
}
@GetMapping("/permission")
public String showPermissionPage() {
return "permissionPage";
}
}

View File

@ -0,0 +1,39 @@
package com.spring.step3.controller.test;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "检查接口", description = "检查当前用户的权限信息")
@RestController
@RequestMapping("/api/security")
public class CheckController {
@Operation(summary = "当前用户的信息", description = "当前用户的信息")
@GetMapping("/current-user")
public Authentication getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("Current user: " + auth.getName());
System.out.println("Authorities: " + auth.getAuthorities());
return auth;
}
@Operation(summary = "获取用户详情", description = "获取用户详情")
@GetMapping("user-detail")
public UserDetails getCurrentUserDetail() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Object principal = auth.getPrincipal();
if (principal instanceof UserDetails) {
return (UserDetails) principal;
} else {
return null;
}
}
}

View File

@ -0,0 +1,41 @@
package com.spring.step3.controller.test;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.security.annotation.IsAdmin;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "ADMIN接口", description = "只要包含 Admin 角色都可以访问")
@Slf4j
@RestController
@RequestMapping("/api/security/admin")
public class SecurityAdminController {
@IsAdmin
@Operation(summary = "拥有 IsAdmin 的角色可以访问", description = "当前用户拥有 IsAdmin 角色可以访问这个接口")
@GetMapping("role-user")
public Result<String> roleUser() {
return Result.success();
}
@IsAdmin
@Operation(summary = "拥有 IsAdmin 的角色可以访问", description = "当前用户拥有 IsAdmin 角色可以访问这个接口")
@GetMapping("upper-user")
public Result<String> upperUser() {
String data = "是区分大小写的";
return Result.success(data);
}
@IsAdmin
@Operation(summary = "拥有 IsAdmin 的角色可以访问", description = "当前用户拥有 IsAdmin 角色可以访问这个接口")
@GetMapping("lower-user")
public Result<String> lowerUser() {
String data = "如果是大写,但是在这里是小写无法访问";
return Result.success(data);
}
}

View File

@ -0,0 +1,50 @@
package com.spring.step3.controller.test;
import com.spring.step3.domain.vo.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "NORMAl接口", description = "测试用的NORMAl接口")
@Slf4j
@RestController
@RequestMapping("/api/security/normal")
public class SecurityController {
@PreAuthorize("hasAuthority('role::read')")
@Operation(summary = "拥有 role:read 的角色可以访问", description = "当前用户拥有 role:read 角色可以访问这个接口")
@GetMapping("role-user")
public Result<String> roleUser() {
return Result.success();
}
@PreAuthorize("hasAuthority('USER')")
@Operation(summary = "拥有 USER 的角色可以访问", description = "当前用户拥有 USER 角色可以访问这个接口")
@GetMapping("upper-user")
public Result<String> upperUser() {
String data = "是区分大小写的";
return Result.success(data);
}
@PreAuthorize("hasAuthority('user')")
@Operation(summary = "拥有 USER 的角色可以访问", description = "当前用户拥有 USER 角色可以访问这个接口")
@GetMapping("lower-user")
public Result<String> lowerUser() {
String data = "如果是大写,但是在这里是小写无法访问";
return Result.success(data);
}
@PostAuthorize("returnObject.data == authentication.name")
@Operation(summary = "测试使用返回参数判断权限", description = "测试使用返回参数判断权限 用户拥有 role::read 可以访问这个接口")
@GetMapping("test-post-authorize")
public Result<String> testPostAuthorize() {
log.info("方法内容已经执行。。。");
String data = "Bunny";
return Result.success(data);
}
}

View File

@ -0,0 +1,40 @@
package com.spring.step3.controller.test;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.security.annotation.HasAnyAuthority;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "ANY ROLES", description = "只要包含 ANY 角色都可以访问")
@Slf4j
@RestController
@RequestMapping("/api/security/any")
public class SecurityHasAnyAuthorityController {
@HasAnyAuthority(auth = {"'USER'", "'ADMIN'"})
@Operation(summary = "拥有 HasAnyXXX 的角色可以访问", description = "当前用户拥有 HasAnyXXX 角色可以访问这个接口")
@GetMapping("role-user")
public Result<String> roleUser() {
return Result.success();
}
@HasAnyAuthority(auth = {"'USER'", "'ADMIN'"})
@Operation(summary = "拥有 HasAnyXXX 的角色可以访问", description = "当前用户拥有 HasAnyXXX 角色可以访问这个接口")
@GetMapping("upper-user")
public Result<String> upperUser() {
String data = "是区分大小写的";
return Result.success(data);
}
@HasAnyAuthority(auth = {"'USER'", "'ADMIN'"})
@Operation(summary = "拥有 HasAnyXXX 的角色可以访问", description = "当前用户拥有 HasAnyXXX 角色可以访问这个接口")
@GetMapping("lower-user")
public Result<String> lowerUser() {
String data = "如果是大写,但是在这里是小写无法访问";
return Result.success(data);
}
}

View File

@ -0,0 +1,32 @@
package com.spring.step3.controller.test;
import com.spring.step3.domain.vo.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "Programmatically", description = "只要包含 Programmatically 角色都可以访问")
@Slf4j
@RestController
@RequestMapping("/api/security/programmatically")
public class SecurityProgrammaticallyController {
@Operation(summary = "拥有 USER 的角色可以访问", description = "当前用户拥有 USER 角色可以访问这个接口")
@GetMapping("upper-user")
public Result<String> upperUser() {
String data = "是区分大小写的";
return Result.success(data);
}
@PreAuthorize("@auth.decide(#name)")
@Operation(summary = "拥有 USER 的角色可以访问", description = "当前用户拥有 USER 角色可以访问这个接口")
@GetMapping("lower-user")
public Result<String> lowerUser(String name) {
return Result.success(name);
}
}

View File

@ -0,0 +1,41 @@
package com.spring.step3.controller.test;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.security.annotation.HasUSERAuthorize;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "USER测试", description = "只要包含 USER 角色都可以访问")
@Slf4j
@RestController
@RequestMapping("/api/security/user")
public class SecurityUserController {
@HasUSERAuthorize("role:read")
@Operation(summary = "拥有 role:read 的角色可以访问", description = "当前用户拥有 role:read 角色可以访问这个接口")
@GetMapping("role-user")
public Result<String> roleUser() {
return Result.success();
}
@HasUSERAuthorize("USER")
@Operation(summary = "拥有 USER 的角色可以访问", description = "当前用户拥有 USER 角色可以访问这个接口")
@GetMapping("upper-user")
public Result<String> upperUser() {
String data = "是区分大小写的";
return Result.success(data);
}
@HasUSERAuthorize("user")
@Operation(summary = "拥有 USER 的角色可以访问", description = "当前用户拥有 USER 角色可以访问这个接口")
@GetMapping("lower-user")
public Result<String> lowerUser() {
String data = "如果是大写,但是在这里是小写无法访问";
return Result.success(data);
}
}

View File

@ -0,0 +1,19 @@
package com.spring.step3.domain.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(name = "LoginRequest", title = "LoginRequest登录参数", description = "登录请求参数")
public class LoginDto {
@Schema(name = "type", description = "登录类型")
private String type = "default";
@Schema(name = "username", title = "用户名")
private String username;
@Schema(name = "password", description = "密码")
private String password;
}

View File

@ -0,0 +1,51 @@
package com.spring.step3.domain.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "PermissionDTO对象", title = "系统权限表", description = "系统权限表的DTO对象")
public class PermissionDto {
@Schema(name = "id", title = "主键ID")
private Long id;
@Schema(name = "permissionCode", title = "权限编码")
private String permissionCode;
@Schema(name = "url", description = "URL")
private String url;
@Schema(name = "method", description = "请求方法类型")
private String method;
@Schema(name = "description", title = "权限描述")
private String description;
@Schema(name = "remark", title = "备注信息")
private String remark;
@Schema(name = "createTime", title = "创建时间")
private LocalDateTime createTime;
@Schema(name = "updateTime", title = "更新时间")
private LocalDateTime updateTime;
@Schema(name = "createUser", title = "创建用户ID")
private Long createUser;
@Schema(name = "updateUser", title = "更新用户ID")
private Long updateUser;
@Schema(name = "isDeleted", title = "是否删除0-未删除1-已删除")
private Boolean isDeleted;
}

View File

@ -0,0 +1,28 @@
package com.spring.step3.domain.dto.role;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "AssignRolePermissionDTO对象", title = "角色分配权限DTO", description = "根据角色id分配权限")
public class AssignRolePermissionDto {
@Schema(name = "roleId", title = "角色ID")
@NotNull(message = "角色id为空")
private Long roleId;
@Schema(name = "permissionId", title = "权限ID")
@NotEmpty(message = "权限id为空")
private List<Long> permissionIds;
}

View File

@ -0,0 +1,49 @@
package com.spring.step3.domain.dto.role;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "RoleDTO对象", title = "系统角色表", description = "系统角色表的DTO对象")
public class RoleDto {
@Schema(name = "id", title = "主键ID")
private Long id;
@Schema(name = "roleCode", title = "角色码")
@NotBlank(message = "角色码不能为空")
private String roleCode;
@Schema(name = "description", title = "角色描述")
@NotBlank(message = "角色描述不能为空")
private String description;
@Schema(name = "remark", title = "备注信息")
@NotBlank(message = "备注信息不能为空")
private String remark;
@Schema(name = "createTime", title = "创建时间")
private LocalDateTime createTime;
@Schema(name = "updateTime", title = "更新时间")
private LocalDateTime updateTime;
@Schema(name = "createUser", title = "创建用户ID")
private Long createUser;
@Schema(name = "updateUser", title = "更新用户ID")
private Long updateUser;
@Schema(name = "isDeleted", title = "是否删除0-未删除1-已删除")
private Boolean isDeleted;
}

View File

@ -0,0 +1,25 @@
package com.spring.step3.domain.dto.role;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "RolePermissionDTO对象", title = "角色权限关联表", description = "角色权限关联表的DTO对象")
public class RolePermissionDto {
@Schema(name = "id", title = "主键ID")
private Long id;
@Schema(name = "roleId", title = "角色ID")
private Long roleId;
@Schema(name = "permissionId", title = "权限ID")
private Long permissionId;
}

View File

@ -0,0 +1,28 @@
package com.spring.step3.domain.dto.user;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "AssignUserRoleDTO对象", title = "用户分配角色DTO", description = "根据用户id分配用户角色")
public class AssignUserRoleDto {
@Schema(name = "userId", title = "用户ID")
@NotNull(message = "用户id为空")
private Long userId;
@Schema(name = "roleId", title = "角色ID")
@NotEmpty(message = "角色ID为空")
private List<Long> roleIds;
}

View File

@ -0,0 +1,31 @@
package com.spring.step3.domain.dto.user;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "UserDTO对象", title = "用户", description = "用户的DTO对象")
public class UserDto {
@Schema(name = "id", title = "主键")
private Long id;
@Schema(name = "username", title = "用户名")
@NotBlank(message = "用户名不能为空")
private String username;
@Schema(name = "password", title = "密码")
private String password;
@Schema(name = "email", title = "邮箱")
@NotBlank(message = "邮箱不能为空")
private String email;
}

View File

@ -0,0 +1,25 @@
package com.spring.step3.domain.dto.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "UserRoleDTO对象", title = "用户角色关联表", description = "用户角色关联表的DTO对象")
public class UserRoleDto {
@Schema(name = "id", title = "主键")
private Long id;
@Schema(name = "roleId", title = "角色ID")
private Long roleId;
@Schema(name = "userId", title = "用户ID")
private Long userId;
}

View File

@ -0,0 +1,37 @@
package com.spring.step3.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.spring.step3.domain.entity.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
@Getter
@Setter
@Accessors(chain = true)
@TableName("t_permission")
@Schema(name = "Permission对象", title = "系统权限表", description = "系统权限表的实体类对象")
public class PermissionEntity extends BaseEntity {
@Schema(name = "permissionCode", title = "权限编码")
private String permissionCode;
@Schema(name = "url", description = "URL")
private String url;
@Schema(name = "method", description = "请求方法类型")
private String method;
@Schema(name = "description", title = "权限描述")
private String description;
@Schema(name = "remark", title = "备注信息")
private String remark;
@Schema(name = "isDeleted", title = "是否删除0-未删除1-已删除")
@TableField(exist = false)
private Boolean isDeleted;
}

View File

@ -0,0 +1,31 @@
package com.spring.step3.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.spring.step3.domain.entity.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
@Getter
@Setter
@Accessors(chain = true)
@TableName("t_role")
@Schema(name = "Role对象", title = "系统角色表", description = "系统角色表的实体类对象")
public class RoleEntity extends BaseEntity {
@Schema(name = "roleCode", title = "角色码")
private String roleCode;
@Schema(name = "description", title = "角色描述")
private String description;
@Schema(name = "remark", title = "备注信息")
private String remark;
@Schema(name = "isDeleted", title = "是否删除0-未删除1-已删除")
@TableField(exist = false)
private Boolean isDeleted;
}

View File

@ -0,0 +1,28 @@
package com.spring.step3.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.spring.step3.domain.entity.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
@Getter
@Setter
@Accessors(chain = true)
@TableName("t_role_permission")
@Schema(name = "RolePermission对象", title = "角色权限关联表", description = "角色权限关联表的实体类对象")
public class RolePermissionEntity extends BaseEntity {
@Schema(name = "roleId", title = "角色ID")
private Long roleId;
@Schema(name = "permissionId", title = "权限ID")
private Long permissionId;
@Schema(name = "isDeleted", title = "是否删除0-未删除1-已删除")
@TableField(exist = false)
private Boolean isDeleted;
}

View File

@ -0,0 +1,78 @@
package com.spring.step3.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.time.LocalDateTime;
import java.util.Set;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
@Schema(name = "User对象", title = "用户", description = "用户的实体类对象")
public class UserEntity implements UserDetails, CredentialsContainer {
@Schema(name = "id", title = "唯一标识")
@TableId(value = "id", type = IdType.ASSIGN_ID)
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@Schema(name = "username", title = "用户名")
private String username;
@Schema(name = "password", title = "密码")
private String password;
@Schema(name = "email", title = "邮箱")
private String email;
@Schema(name = "createTime", title = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@Schema(name = "updateTime", title = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@Schema(name = "createUser", title = "创建用户")
@TableField(fill = FieldFill.INSERT)
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long createUser;
@Schema(name = "updateUser", title = "操作用户")
@TableField(fill = FieldFill.INSERT_UPDATE)
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long updateUser;
@Schema(name = "isDeleted", title = "是否被删除")
@TableField(exist = false)
private Boolean isDeleted;
@TableField(exist = false)
private Set<? extends GrantedAuthority> authorities;
@Override
public void eraseCredentials() {
this.password = null;
}
@Override
public Set<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
}

View File

@ -0,0 +1,28 @@
package com.spring.step3.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.spring.step3.domain.entity.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
@Getter
@Setter
@Accessors(chain = true)
@TableName("t_user_role")
@Schema(name = "UserRole对象", title = "用户角色关联表", description = "用户角色关联表的实体类对象")
public class UserRoleEntity extends BaseEntity {
@Schema(name = "roleId", title = "角色ID")
private Long roleId;
@Schema(name = "userId", title = "用户ID")
private Long userId;
@Schema(name = "isDeleted", title = "是否删除0-未删除1-已删除")
@TableField(exist = false)
private Boolean isDeleted;
}

View File

@ -0,0 +1,48 @@
package com.spring.step3.domain.entity.base;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.time.LocalDateTime;
@Getter
@Setter
@Schema(name = "BaseEntity", title = "基础信息字段", description = "基础信息字段")
public class BaseEntity implements Serializable {
@Schema(name = "id", title = "唯一标识")
@TableId(value = "id", type = IdType.ASSIGN_ID)
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@Schema(name = "createTime", title = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@Schema(name = "updateTime", title = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@Schema(name = "createUser", title = "创建用户")
@TableField(fill = FieldFill.INSERT)
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long createUser;
@Schema(name = "updateUser", title = "操作用户")
@TableField(fill = FieldFill.INSERT_UPDATE)
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long updateUser;
}

View File

@ -0,0 +1,18 @@
package com.spring.step3.domain.enums;
import lombok.Getter;
@Getter
public enum LoginEnums {
// 邮箱登录请求
EMAIL_STRATEGY("email"),
// 默认登录请求
default_STRATEGY("default"),
;
private final String value;
LoginEnums(String value) {
this.value = value;
}
}

View File

@ -0,0 +1,39 @@
package com.spring.step3.domain.vo;
import com.spring.step3.domain.vo.base.BaseVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* 用户登录返回内容
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@Schema(name = "LoginVo对象", title = "登录成功返回内容", description = "登录成功返回内容")
public class LoginVo extends BaseVo {
@Schema(name = "nickname", title = "昵称")
private String nickname;
@Schema(name = "username", title = "用户名")
private String username;
@Schema(name = "email", title = "邮箱")
private String email;
@Schema(name = "token", title = "令牌")
private String token;
@Schema(name = "expires", title = "过期时间")
private String expires;
@Schema(name = "readMeDay", title = "记住我多久")
private Long readMeDay;
}

View File

@ -0,0 +1,41 @@
package com.spring.step3.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.spring.step3.domain.vo.base.BaseVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@Schema(name = "PermissionVO对象", title = "系统权限表", description = "系统权限表的VO对象")
public class PermissionVo extends BaseVo {
@Schema(name = "id", title = "主键ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@Schema(name = "permissionCode", title = "权限编码")
private String permissionCode;
@Schema(name = "url", description = "URL")
private String url;
@Schema(name = "method", description = "请求方法类型")
private String method;
@Schema(name = "description", title = "权限描述")
private String description;
@Schema(name = "remark", title = "备注信息")
private String remark;
}

View File

@ -0,0 +1,33 @@
package com.spring.step3.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.spring.step3.domain.vo.base.BaseVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@Schema(name = "RolePermissionVO对象", title = "角色权限关联表", description = "角色权限关联表的VO对象")
public class RolePermissionVo extends BaseVo {
@Schema(name = "id", title = "主键ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@Schema(name = "roleId", title = "角色ID")
private Long roleId;
@Schema(name = "permissionId", title = "权限ID")
private Long permissionId;
}

View File

@ -0,0 +1,35 @@
package com.spring.step3.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.spring.step3.domain.vo.base.BaseVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@Schema(name = "RoleVO对象", title = "系统角色表", description = "系统角色表的VO对象")
public class RoleVo extends BaseVo {
@Schema(name = "id", title = "主键ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@Schema(name = "roleCode", title = "角色名称")
private String roleCode;
@Schema(name = "description", title = "角色描述")
private String description;
@Schema(name = "remark", title = "备注信息")
private String remark;
}

View File

@ -0,0 +1,36 @@
package com.spring.step3.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.spring.step3.domain.vo.base.BaseVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@Schema(name = "UserRoleVO对象", title = "用户角色关联表", description = "用户角色关联表的VO对象")
public class UserRoleVo extends BaseVo {
@Schema(name = "id", title = "主键")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@Schema(name = "roleId", title = "角色ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long roleId;
@Schema(name = "userId", title = "用户ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
}

View File

@ -0,0 +1,32 @@
package com.spring.step3.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.spring.step3.domain.vo.base.BaseVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@Schema(name = "UserVO对象", title = "用户基本信息表", description = "用户基本信息表的VO对象")
public class UserVo extends BaseVo {
@Schema(name = "id", title = "主键")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@Schema(name = "username", title = "用户名")
private String username;
@Schema(name = "email", title = "邮箱")
private String email;
}

View File

@ -0,0 +1,23 @@
package com.spring.step3.domain.vo.base;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class BaseVo {
@Schema(name = "createTime", title = "创建时间")
private LocalDateTime createTime;
@Schema(name = "updateTime", title = "更新时间")
private LocalDateTime updateTime;
@Schema(name = "createUser", title = "创建用户ID")
private Long createUser;
@Schema(name = "updateUser", title = "更新用户ID")
private Long updateUser;
}

View File

@ -0,0 +1,37 @@
package com.spring.step3.domain.vo.result;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 封装分页查询结果
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "PageResult 对象", title = "分页返回结果", description = "分页返回结果")
public class PageResult<T> implements Serializable {
@Schema(name = "pageNo", title = "当前页")
private Long pageNo;
@Schema(name = "pageSize", title = "每页记录数")
private Long pageSize;
@Schema(name = "pages", title = "总分页数")
private Long pages;
@Schema(name = "total", title = "总记录数")
private Long total;
@Schema(name = "list", title = "当前页数据集合")
private List<T> list;
}

View File

@ -0,0 +1,174 @@
package com.spring.step3.domain.vo.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
// 状态码
private Integer code;
// 返回消息
private String message;
// 返回数据
private T data;
/**
* * 自定义返回体
*
* @param data 返回体
* @return Result<T>
*/
protected static <T> Result<T> build(T data) {
Result<T> result = new Result<>();
result.setData(data);
return result;
}
/**
* * 自定义返回体使用ResultCodeEnum构建
*
* @param body 返回体
* @param codeEnum 返回状态码
* @return Result<T>
*/
public static <T> Result<T> build(T body, ResultCodeEnum codeEnum) {
Result<T> result = build(body);
result.setCode(codeEnum.getCode());
result.setMessage(codeEnum.getMessage());
return result;
}
/**
* * 自定义返回体
*
* @param body 返回体
* @param code 返回状态码
* @param message 返回消息
* @return Result<T>
*/
public static <T> Result<T> build(T body, Integer code, String message) {
Result<T> result = build(body);
result.setCode(code);
result.setMessage(message);
result.setData(null);
return result;
}
/**
* * 操作成功
*
* @return Result<T>
*/
public static <T> Result<T> success() {
return success(null, ResultCodeEnum.SUCCESS);
}
/**
* * 操作成功
*
* @param data baseCategory1List
*/
public static <T> Result<T> success(T data) {
return build(data, ResultCodeEnum.SUCCESS);
}
/**
* * 操作成功-状态码
*
* @param codeEnum 状态码
*/
public static <T> Result<T> success(ResultCodeEnum codeEnum) {
return success(null, codeEnum);
}
/**
* * 操作成功-自定义返回数据和状态码
*
* @param data 返回体
* @param codeEnum 状态码
*/
public static <T> Result<T> success(T data, ResultCodeEnum codeEnum) {
return build(data, codeEnum);
}
/**
* * 操作失败-自定义返回数据和状态码
*
* @param data 返回体
* @param message 错误信息
*/
public static <T> Result<T> success(T data, String message) {
return build(data, 200, message);
}
/**
* * 操作失败-自定义返回数据和状态码
*
* @param data 返回体
* @param code 状态码
* @param message 错误信息
*/
public static <T> Result<T> success(T data, Integer code, String message) {
return build(data, code, message);
}
/**
* * 操作失败
*/
public static <T> Result<T> error() {
return Result.build(null);
}
/**
* * 操作失败-自定义返回数据
*
* @param data 返回体
*/
public static <T> Result<T> error(T data) {
return build(data, ResultCodeEnum.FAIL);
}
/**
* * 操作失败-状态码
*
* @param codeEnum 状态码
*/
public static <T> Result<T> error(ResultCodeEnum codeEnum) {
return build(null, codeEnum);
}
/**
* * 操作失败-自定义返回数据和状态码
*
* @param data 返回体
* @param codeEnum 状态码
*/
public static <T> Result<T> error(T data, ResultCodeEnum codeEnum) {
return build(data, codeEnum);
}
/**
* * 操作失败-自定义返回数据和状态码
*
* @param data 返回体
* @param code 状态码
* @param message 错误信息
*/
public static <T> Result<T> error(T data, Integer code, String message) {
return build(data, code, message);
}
/**
* * 操作失败-自定义返回数据和状态码
*
* @param data 返回体
* @param message 错误信息
*/
public static <T> Result<T> error(T data, String message) {
return build(null, 500, message);
}
}

View File

@ -0,0 +1,90 @@
package com.spring.step3.domain.vo.result;
import lombok.Getter;
/**
* 统一返回结果状态信息类
*/
@Getter
public enum ResultCodeEnum {
// 成功操作 200
SUCCESS(200, "操作成功"),
LOAD_FINISHED(200, "加载完成"),
ADD_SUCCESS(200, "添加成功"),
UPDATE_SUCCESS(200, "修改成功"),
DELETE_SUCCESS(200, "删除成功"),
ASSIGN_SUCCESS(200, "排序成功"),
SUCCESS_UPLOAD(200, "上传成功"),
SUCCESS_LOGOUT(200, "退出成功"),
EMAIL_CODE_REFRESH(200, "邮箱验证码已刷新"),
EMAIL_CODE_SEND_SUCCESS(200, "邮箱验证码已发送"),
// 验证错误 201
USERNAME_OR_PASSWORD_NOT_EMPTY(201, "用户名或密码不能为空"),
EMAIL_CODE_NOT_EMPTY(201, "邮箱验证码不能为空"),
SEND_EMAIL_CODE_NOT_EMPTY(201, "请先发送邮箱验证码"),
EMAIL_CODE_NOT_MATCHING(201, "邮箱验证码不匹配"),
LOGIN_ERROR(500, "账号或密码错误"),
LOGIN_ERROR_USERNAME_PASSWORD_NOT_EMPTY(201, "登录信息不能为空"),
GET_BUCKET_EXCEPTION(201, "获取文件信息失败"),
SEND_MAIL_CODE_ERROR(201, "邮件发送失败"),
EMAIL_CODE_EMPTY(201, "邮箱验证码过期或不存在"),
EMAIL_EXIST(201, "邮箱已存在"),
REQUEST_IS_EMPTY(201, "请求数据为空"),
DATA_TOO_LARGE(201, "请求数据为空"),
UPDATE_NEW_PASSWORD_SAME_AS_OLD_PASSWORD(201, "新密码与密码相同"),
// 数据相关 206
ILLEGAL_REQUEST(206, "非法请求"),
REPEAT_SUBMIT(206, "重复提交"),
DATA_ERROR(206, "数据异常"),
EMAIL_USER_TEMPLATE_IS_EMPTY(206, "邮件模板为空"),
EMAIL_TEMPLATE_IS_EMPTY(206, "邮件模板为空"),
EMAIL_USER_IS_EMPTY(206, "关联邮件用户配置为空"),
DATA_EXIST(206, "数据已存在"),
DATA_NOT_EXIST(206, "数据不存在"),
ALREADY_USER_EXCEPTION(206, "用户已存在"),
USER_IS_EMPTY(206, "用户不存在"),
FILE_NOT_EXIST(206, "文件不存在"),
NEW_PASSWORD_SAME_OLD_PASSWORD(206, "新密码不能和旧密码相同"),
MISSING_TEMPLATE_FILES(206, "缺少模板文件"),
// 身份过期 208
LOGIN_AUTH(208, "请先登陆"),
AUTHENTICATION_EXPIRED(208, "身份验证过期"),
SESSION_EXPIRATION(208, "会话过期"),
// 209
THE_SAME_USER_HAS_LOGGED_IN(209, "相同用户已登录"),
// 提示错误
UPDATE_ERROR(216, "修改失败"),
URL_ENCODE_ERROR(216, "URL编码失败"),
ILLEGAL_CALLBACK_REQUEST_ERROR(217, "非法回调请求"),
FETCH_USERINFO_ERROR(219, "获取用户信息失败"),
ILLEGAL_DATA_REQUEST(219, "非法数据请求"),
CLASS_NOT_FOUND(219, "类名不存在"),
ADMIN_ROLE_CAN_NOT_DELETED(219, "无法删除admin角色"),
ROUTER_RANK_NEED_LARGER_THAN_THE_PARENT(219, "设置路由等级需要大于或等于父级的路由等级"),
// 无权访问 403
FAIL_NO_ACCESS_DENIED(403, "无权访问"),
FAIL_NO_ACCESS_DENIED_USER_OFFLINE(403, "用户强制下线"),
TOKEN_PARSING_FAILED(403, "token解析失败"),
FAIL_NO_ACCESS_DENIED_USER_LOCKED(403, "该账户已封禁"),
// 系统错误 500
UNKNOWN_EXCEPTION(500, "服务异常"),
SERVICE_ERROR(500, "服务异常"),
UPLOAD_ERROR(500, "上传失败"),
FAIL(500, "失败"),
;
private final Integer code;
private final String message;
ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}

View File

@ -0,0 +1,46 @@
package com.spring.step3.exception;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
@NoArgsConstructor
@Getter
@ToString
@Slf4j
public class AuthenticSecurityException extends RuntimeException {
// 状态码
Integer code;
// 描述信息
String message = "服务异常";
// 返回结果状态
ResultCodeEnum resultCodeEnum;
public AuthenticSecurityException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
public AuthenticSecurityException(String message) {
super(message);
this.message = message;
}
public AuthenticSecurityException(ResultCodeEnum codeEnum) {
super(codeEnum.getMessage());
this.code = codeEnum.getCode();
this.message = codeEnum.getMessage();
this.resultCodeEnum = codeEnum;
}
public AuthenticSecurityException(String message, Exception exception) {
super(message);
this.message = message;
log.error(message, exception);
}
}

View File

@ -0,0 +1,112 @@
package com.spring.step3.exception;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* 全局异常拦截器
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 自定义异常信息
@ExceptionHandler(AuthenticSecurityException.class)
@ResponseBody
public Result<Object> exceptionHandler(AuthenticSecurityException exception) {
Integer code = exception.getCode() != null ? exception.getCode() : 500;
return Result.error(null, code, exception.getMessage());
}
// 运行时异常信息
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public Result<Object> exceptionHandler(RuntimeException exception) {
String message = exception.getMessage();
message = StringUtils.hasText(message) ? message : "服务器异常";
exception.printStackTrace();
// 解析异常
String jsonParseError = "JSON parse error (.*)";
Matcher jsonParseErrorMatcher = Pattern.compile(jsonParseError).matcher(message);
if (jsonParseErrorMatcher.find()) {
return Result.error(null, 500, "JSON解析异常 " + jsonParseErrorMatcher.group(1));
}
// 数据过大
String dataTooLongError = "Data too long for column (.*?) at row 1";
Matcher dataTooLongErrorMatcher = Pattern.compile(dataTooLongError).matcher(message);
if (dataTooLongErrorMatcher.find()) {
return Result.error(null, 500, dataTooLongErrorMatcher.group(1) + " 字段数据过大");
}
// 主键冲突
String primaryKeyError = "Duplicate entry '(.*?)' for key .*";
Matcher primaryKeyErrorMatcher = Pattern.compile(primaryKeyError).matcher(message);
if (primaryKeyErrorMatcher.find()) {
return Result.error(null, 500, "[" + primaryKeyErrorMatcher.group(1) + "]已存在");
}
// corn表达式错误
String cronExpression = "CronExpression '(.*?)' is invalid";
Matcher cronExpressionMatcher = Pattern.compile(cronExpression).matcher(message);
if (cronExpressionMatcher.find()) {
return Result.error(null, 500, "表达式 " + cronExpressionMatcher.group(1) + " 不合法");
}
log.error("GlobalExceptionHandler===>运行时异常信息:{}", message);
return Result.error(null, 500, message);
}
// 表单验证字段
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
String errorMessage = ex.getBindingResult().getFieldErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.distinct()
.collect(Collectors.joining(", "));
return Result.error(null, 201, errorMessage);
}
// 特定异常处理
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public Result<Object> error(ArithmeticException exception) {
log.error("GlobalExceptionHandler===>特定异常信息:{}", exception.getMessage());
return Result.error(null, 500, exception.getMessage());
}
// 处理SQL异常
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
@ResponseBody
public Result<String> exceptionHandler(SQLIntegrityConstraintViolationException exception) {
log.error("GlobalExceptionHandler===>处理SQL异常:{}", exception.getMessage());
String message = exception.getMessage();
if (message.contains("Duplicate entry")) {
// 错误信息
return Result.error(ResultCodeEnum.USER_IS_EMPTY);
} else {
return Result.error(ResultCodeEnum.UNKNOWN_EXCEPTION);
}
}
// 处理无权访问异常
@ExceptionHandler(AccessDeniedException.class)
public Result<String> handleAccessDenied(AccessDeniedException exception) {
return Result.error(exception.getMessage(), ResultCodeEnum.FAIL_NO_ACCESS_DENIED);
}
}

View File

@ -0,0 +1,32 @@
package com.spring.step3.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring.step3.domain.dto.permission.PermissionDto;
import com.spring.step3.domain.entity.PermissionEntity;
import com.spring.step3.domain.vo.PermissionVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* <p>
* 系统权限表 Mapper 接口
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@Mapper
public interface PermissionMapper extends BaseMapper<PermissionEntity> {
/**
* * 分页查询系统权限表内容
*
* @param pageParams 系统权限表分页参数
* @param dto 系统权限表查询表单
* @return 系统权限表分页结果
*/
IPage<PermissionVo> selectListByPage(@Param("page") Page<PermissionEntity> pageParams, @Param("dto") PermissionDto dto);
}

View File

@ -0,0 +1,32 @@
package com.spring.step3.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring.step3.domain.dto.role.RoleDto;
import com.spring.step3.domain.entity.RoleEntity;
import com.spring.step3.domain.vo.RoleVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* <p>
* 系统角色表 Mapper 接口
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@Mapper
public interface RoleMapper extends BaseMapper<RoleEntity> {
/**
* * 分页查询系统角色表内容
*
* @param pageParams 系统角色表分页参数
* @param dto 系统角色表查询表单
* @return 系统角色表分页结果
*/
IPage<RoleVo> selectListByPage(@Param("page") Page<RoleEntity> pageParams, @Param("dto") RoleDto dto);
}

View File

@ -0,0 +1,49 @@
package com.spring.step3.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring.step3.domain.dto.role.RolePermissionDto;
import com.spring.step3.domain.entity.RolePermissionEntity;
import com.spring.step3.domain.vo.RolePermissionVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* 角色权限关联表 Mapper 接口
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@Mapper
public interface RolePermissionMapper extends BaseMapper<RolePermissionEntity> {
/**
* * 分页查询角色权限关联表内容
*
* @param pageParams 角色权限关联表分页参数
* @param dto 角色权限关联表查询表单
* @return 角色权限关联表分页结果
*/
IPage<RolePermissionVo> selectListByPage(@Param("page") Page<RolePermissionEntity> pageParams, @Param("dto") RolePermissionDto dto);
/**
* 根据角色id获取权限内容
*
* @param permissionId 权限id
* @return 角色权限列表
*/
List<RolePermissionEntity> selectListByPermissionId(Long permissionId);
/**
* 先删除当前已经分配的角色权限内容
*
* @param roleId 角色id
*/
void deleteByRoleId(Long roleId);
}

View File

@ -0,0 +1,60 @@
package com.spring.step3.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring.step3.domain.dto.user.UserDto;
import com.spring.step3.domain.entity.PermissionEntity;
import com.spring.step3.domain.entity.RoleEntity;
import com.spring.step3.domain.entity.UserEntity;
import com.spring.step3.domain.vo.UserVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* 用户基本信息表 Mapper 接口
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@Mapper
public interface UserMapper extends BaseMapper<UserEntity> {
/**
* * 分页查询用户基本信息表内容
*
* @param pageParams 用户基本信息表分页参数
* @param dto 用户基本信息表查询表单
* @return 用户基本信息表分页结果
*/
IPage<UserVo> selectListByPage(@Param("page") Page<UserEntity> pageParams, @Param("dto") UserDto dto);
/**
* 根据用户id查找当前用户的权限
*
* @param userId 用户id
* @return 权限列表
*/
List<PermissionEntity> selectPermissionByUserId(Long userId);
/**
* 根据用户名查询当前用户
*
* @param username 用户名
* @return 用户 {@link UserEntity}
*/
UserEntity selectByUsername(String username);
/**
* 根据用户id查找该用户的角色内容
*
* @param userId 用户id
* @return 当前用户的角色信息
*/
List<RoleEntity> selectRolesByUserId(Long userId);
}

View File

@ -0,0 +1,49 @@
package com.spring.step3.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring.step3.domain.dto.user.UserRoleDto;
import com.spring.step3.domain.entity.UserRoleEntity;
import com.spring.step3.domain.vo.UserRoleVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* 用户角色关联表 Mapper 接口
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@Mapper
public interface UserRoleMapper extends BaseMapper<UserRoleEntity> {
/**
* * 分页查询用户角色关联表内容
*
* @param pageParams 用户角色关联表分页参数
* @param dto 用户角色关联表查询表单
* @return 用户角色关联表分页结果
*/
IPage<UserRoleVo> selectListByPage(@Param("page") Page<UserRoleEntity> pageParams, @Param("dto") UserRoleDto dto);
/**
* 根据用户id获取当前用户角色列表
*
* @param userId 用户id
* @return 用户和角色列表
*/
List<UserRoleEntity> getRoleListByUserId(Long userId);
/**
* 根据用户id删除用户相关分配角色
*
* @param userId 用户id
*/
void deleteByUserId(Long userId);
}

View File

@ -0,0 +1,15 @@
package com.spring.step3.security.annotation;
import org.springframework.security.access.prepost.PreAuthorize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasAnyAuthority({auth})")
public @interface HasAnyAuthority {
String[] auth();
}

View File

@ -0,0 +1,15 @@
package com.spring.step3.security.annotation;
import org.springframework.security.access.prepost.PreAuthorize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasAuthority('USER') || hasAuthority('{value}')")
public @interface HasUSERAuthorize {
String value();
}

View File

@ -0,0 +1,17 @@
package com.spring.step3.security.annotation;
import org.springframework.security.access.prepost.PreAuthorize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 判断当前是否是Admin用户
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasAuthority('ADMIN')")
public @interface IsAdmin {
}

View File

@ -0,0 +1,14 @@
package com.spring.step3.security.annotation.programmatically;
import org.springframework.stereotype.Component;
@Component("auth")
public class AuthorizationLogic {
public boolean decide(String name) {
System.out.println(name);
// 直接使用name的实现
return name.equalsIgnoreCase("user");
}
}

View File

@ -0,0 +1,21 @@
package com.spring.step3.security.config;
import org.springframework.context.annotation.Configuration;
@Configuration
// @EnableMethodSecurity(prePostEnabled = false)
public class AuthorizationManagerConfiguration {
// @Bean
// @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
// Advisor preAuthorize(PreAuthorizationManager manager) {
// return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
// }
//
// @Bean
// @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
// Advisor postAuthorize(PostAuthorizationManager manager) {
// return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
// }
}

View File

@ -0,0 +1,71 @@
package com.spring.step3.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfiguration {
/**
* 注册一个用于Spring Security预授权/后授权的模板元注解默认配置Bean
*
* <p>该Bean提供了基于SpEL表达式的权限校验模板可用于自定义组合注解</p>
*
* <h3>典型用法</h3>
* <p>通过此配置可以简化自定义权限注解的定义例如</p>
* <pre>{@code
* &#064;Target({ElementType.METHOD, ElementType.TYPE})
* &#064;Retention(RetentionPolicy.RUNTIME)
* &#064;PreAuthorize("hasAnyAuthority( // 使用模板提供的表达式语法
* public @interface HasAnyAuthority {
* String[] auth(); // 接收权限列表参数
* }
* }</pre>
*
* <h3>注意事项</h3>
* <ul>
* <li>需要确保Spring Security的预授权功能已启用</li>
* <li>模板表达式应符合SpEL语法规范</li>
* </ul>
*
* @return PrePostTemplateDefaults 实例用于预/后授权注解的默认配置
* @see org.springframework.security.access.prepost.PreAuthorize
* @see org.springframework.security.access.prepost.PostAuthorize
*/
@Bean
PrePostTemplateDefaults prePostTemplateDefaults() {
return new PrePostTemplateDefaults();
}
/**
* 配置密码编码器Bean
*
* <p>Spring Security提供了多种密码编码器实现推荐使用BCryptPasswordEncoder作为默认选择</p>
*
* <p>特点</p>
* <ul>
* <li>BCryptPasswordEncoder - 使用bcrypt强哈希算法自动加盐是当前最推荐的密码编码器</li>
* <li>Argon2PasswordEncoder - 使用Argon2算法抗GPU/ASIC攻击但需要更多内存</li>
* <li>SCryptPasswordEncoder - 使用scrypt算法内存密集型抗硬件攻击</li>
* <li>Pbkdf2PasswordEncoder - 使用PBKDF2算法较老但广泛支持</li>
* </ul>
*
* <p>注意不推荐使用MD5等弱哈希算法Spring官方也不推荐自定义弱密码编码器</p>
*
* @return PasswordEncoder 密码编码器实例
* @see BCryptPasswordEncoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
// 其他编码器示例根据需求选择一种:
// return new Argon2PasswordEncoder(16, 32, 1, 1 << 14, 2);
// return new SCryptPasswordEncoder();
// return new Pbkdf2PasswordEncoder("secret", 185000, 256);
// 实际项目中只需返回一个密码编码器
return new BCryptPasswordEncoder();
}
}

View File

@ -0,0 +1,106 @@
package com.spring.step3.security.config;
import com.spring.step3.security.filter.JwtAuthenticationFilter;
import com.spring.step3.security.handler.SecurityAccessDeniedHandler;
import com.spring.step3.security.handler.SecurityAuthenticationEntryPoint;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(jsr250Enabled = true)
@RequiredArgsConstructor
public class SecurityWebConfiguration {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
// @Bean
// SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// http.authorizeHttpRequests(authorizeRequests ->
// // 访问路径为 /api 时需要进行认证
// authorizeRequests
// // 只认证 /api/** 下的所有接口
// .requestMatchers("/api/**").authenticated()
// // 其余请求都放行
// .anyRequest().permitAll()
// )
// .formLogin(loginPage -> loginPage
// // 自定义登录页路径
// .loginPage("/login-page")
// // 处理登录的URL默认就是/login
// .loginProcessingUrl("/login")
// // 登录成功跳转
// .defaultSuccessUrl("/")
// // 登录失败跳转
// .failureUrl("/login-page?error=true")
// .permitAll()
// )
// // 使用默认的登录
// // .formLogin(Customizer.withDefaults())
// // 禁用表单登录
// // .formLogin(AbstractHttpConfigurer::disable)
// .logout(logout -> logout
// .logoutSuccessUrl("/login-page?logout=true")
// .permitAll()
// )
// .csrf(AbstractHttpConfigurer::disable)
// .exceptionHandling(exception -> {
// // 请求未授权接口
// exception.authenticationEntryPoint(new SecurityAuthenticationEntryPoint());
// // 没有权限访问
// exception.accessDeniedHandler(new SecurityAccessDeniedHandler());
// })
// ;
//
// return http.build();
// }
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 前端段分离不需要---禁用明文验证
.httpBasic(AbstractHttpConfigurer::disable)
// 前端段分离不需要---禁用默认登录页
.formLogin(AbstractHttpConfigurer::disable)
// 前端段分离不需要---禁用退出页
.logout(AbstractHttpConfigurer::disable)
// 前端段分离不需要---csrf攻击
.csrf(AbstractHttpConfigurer::disable)
// 跨域访问权限如果需要可以关闭后自己配置跨域访问
.cors(AbstractHttpConfigurer::disable)
// 前后端分离不需要---因为是无状态的
// .sessionManagement(AbstractHttpConfigurer::disable)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authorizeHttpRequests(authorizeRequests ->
// 访问路径为 /api 时需要进行认证
authorizeRequests
// 不认证登录接口
.requestMatchers("/*/login", "/api/security/**").permitAll()
// 只认证 /api/** 下的所有接口
.requestMatchers("/api/**").authenticated()
// 其余请求都放行
.anyRequest().permitAll()
)
.exceptionHandling(exception -> {
// 请求未授权接口
exception.authenticationEntryPoint(new SecurityAuthenticationEntryPoint());
// 没有权限访问
exception.accessDeniedHandler(new SecurityAccessDeniedHandler());
})
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilter(jwtAuthenticationFilter)
;
return http.build();
}
}

View File

@ -0,0 +1,63 @@
package com.spring.step3.security.event;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.context.event.EventListener;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
@Slf4j
@Component
public class AuthenticationEvents {
/**
* 监听拒绝授权内容
*
* @param failure 授权失败
*/
@EventListener
public void onFailure(AuthorizationDeniedEvent<MethodInvocation> failure) {
try {
// getSource getObject意思一样一种是传入泛型自动转换一种是要手动转换
Object source = failure.getSource();
// 直接获取泛型对象
MethodInvocation methodInvocation = failure.getObject();
Method method = methodInvocation.getMethod();
Object[] args = methodInvocation.getArguments();
log.warn("方法调用被拒绝: {}.{}, 参数: {}",
method.getDeclaringClass().getSimpleName(),
method.getName(),
Arrays.toString(args));
// 这里面的信息和接口 /api/security/current-user 内容一样
Authentication authentication = failure.getAuthentication().get();
AuthorizationDecision authorizationDecision = failure.getAuthorizationDecision();
// ExpressionAuthorizationDecision [granted=false, expressionAttribute=hasAuthority('ADMIN')]
System.out.println(authorizationDecision);
log.warn("授权失败 - 用户: {}, 权限: {}", authentication.getName(), authorizationDecision);
} catch (Exception e) {
log.info(e.getMessage());
}
}
/**
* 监听授权的内容
* 如果要监听授权成功的内容这个内容可能相当的多毕竟正常情况授权成功的内容还是比较多的
* 既然内容很多又要监听如果真的需要一定要处理好业务逻辑不要被成功的消息淹没
*
* @param success 授权成功
*/
@EventListener
public void onSuccess(AuthorizationGrantedEvent<MethodInvocation> success) {
}
}

View File

@ -0,0 +1,21 @@
package com.spring.step3.security.event;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.stereotype.Component;
/**
* 如果要监听授权和拒绝的授权需要发布一个像下面这样的事件
* 之后使用 Spring @EventListener
*/
@Component
public class SecurityAuthorizationPublisher {
@Bean
public AuthorizationEventPublisher authorizationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
return new SpringAuthorizationEventPublisher(applicationEventPublisher);
}
}

View File

@ -1,10 +1,10 @@
package com.spring.step2.security.filter;
package com.spring.step3.security.filter;
import com.spring.step2.config.context.BaseContext;
import com.spring.step2.domain.vo.result.ResultCodeEnum;
import com.spring.step2.exception.SecurityException;
import com.spring.step2.security.service.DbUserDetailService;
import com.spring.step2.security.service.JwtBearTokenService;
import com.spring.step3.config.context.BaseContext;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.exception.AuthenticSecurityException;
import com.spring.step3.security.service.DbUserDetailService;
import com.spring.step3.security.service.JwtBearTokenService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
@ -47,7 +47,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
// 检查当前Token是否过期
if (jwtBearTokenService.isTokenValid(jwtToken)) {
throw new SecurityException(ResultCodeEnum.AUTHENTICATION_EXPIRED);
throw new AuthenticSecurityException(ResultCodeEnum.AUTHENTICATION_EXPIRED);
}
// 解析当前Token中的用户名

View File

@ -1,9 +1,9 @@
package com.spring.step2.security.handler;
package com.spring.step3.security.handler;
import com.spring.step2.domain.vo.result.Result;
import com.spring.step2.domain.vo.result.ResultCodeEnum;
import com.spring.step2.security.service.JwtBearTokenService;
import com.spring.step2.utils.ResponseUtil;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.security.service.JwtBearTokenService;
import com.spring.step3.utils.ResponseUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

View File

@ -0,0 +1,26 @@
package com.spring.step3.security.handler;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.utils.ResponseUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import java.io.IOException;
@Slf4j
public class SecurityAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
log.error("SecurityAccessDeniedHandler:{}", accessDeniedException.getLocalizedMessage());
// 无权访问接口
Result<Object> result = Result.error(accessDeniedException.getMessage(), ResultCodeEnum.LOGIN_AUTH);
ResponseUtil.out(response, result);
}
}

View File

@ -0,0 +1,25 @@
package com.spring.step3.security.handler;
import com.spring.step3.domain.vo.result.Result;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.utils.ResponseUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import java.io.IOException;
@Slf4j
public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
log.error("SecurityAuthenticationEntryPoint:{}", authException.getLocalizedMessage());
// 未认证---未登录
Result<Object> result = Result.error(authException.getMessage(), ResultCodeEnum.LOGIN_AUTH);
ResponseUtil.out(response, result);
}
}

View File

@ -0,0 +1,48 @@
package com.spring.step3.security.manger;
/**
* 处理方法调用后的授权检查
* check()方法接收的是MethodInvocationResult对象包含已执行方法的结果
* 用于决定是否允许返回某个方法的结果(后置过滤)
* 这是Spring Security较新的"后置授权"功能
*/
// @Component
// public class PostAuthorizationManager implements AuthorizationManager<MethodInvocationResult> {
//
// /**
// * 这里两个实现方法按照Security官方要求进行实现
// * <h4>类说明</h4>
// * 下面的实现是对方法执行前进行权限校验的判断
// * <pre>
// * <code>AuthorizationManager &ltMethodInvocation></code>
// * </pre>
// * 下面的这个是对方法执行后对权限的判断
// * <pre>
// * <code>AuthorizationManager &ltMethodInvocationResult></code>
// * </pre>
// *
// * <h4>注意事项</h4>
// * 将上述两个方法按照自定义的方式进行实现后还需要禁用默认的
// * <pre>
// * &#064;Configuration
// * &#064;EnableMethodSecurity(prePostEnabled = false)
// * class MethodSecurityConfig {
// * &#064;Bean
// * &#064;Role(BeanDefinition.ROLE_INFRASTRUCTURE)
// * Advisor preAuthorize(MyAuthorizationManager manager) {
// * return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
// * }
// *
// * &#064;Bean
// * &#064;Role(BeanDefinition.ROLE_INFRASTRUCTURE)
// * Advisor postAuthorize(MyAuthorizationManager manager) {
// * return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
// * }
// * }
// * </pre>
// */
// @Override
// public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocationResult invocation) {
// return new AuthorizationDecision(true);
// }
// }

View File

@ -0,0 +1,49 @@
package com.spring.step3.security.manger;
/**
* 处理方法调用前的授权检查
* check()方法接收的是MethodInvocation对象包含即将执行的方法调用信息
* 用于决定是否允许执行某个方法
* 这是传统的"前置授权"模式
*/
// @Component
// public class PreAuthorizationManager implements AuthorizationManager<MethodInvocation> {
//
// /**
// * 这里两个实现方法按照Security官方要求进行实现
// * <h4>类说明</h4>
// * 下面的实现是对方法执行前进行权限校验的判断
// * <pre>
// * <code>AuthorizationManager &ltMethodInvocation></code>
// * </pre>
// * 下面的这个是对方法执行后对权限的判断
// * <pre>
// * <code>AuthorizationManager &ltMethodInvocationResult></code>
// * </pre>
// *
// * <h4>注意事项</h4>
// * 将上述两个方法按照自定义的方式进行实现后还需要禁用默认的
// * <pre>
// * &#064;Configuration
// * &#064;EnableMethodSecurity(prePostEnabled = false)
// * class MethodSecurityConfig {
// * &#064;Bean
// * &#064;Role(BeanDefinition.ROLE_INFRASTRUCTURE)
// * Advisor preAuthorize(MyAuthorizationManager manager) {
// * return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
// * }
// *
// * &#064;Bean
// * &#064;Role(BeanDefinition.ROLE_INFRASTRUCTURE)
// * Advisor postAuthorize(MyAuthorizationManager manager) {
// * return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
// * }
// * }
// * </pre>
// */
// @Override
// public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
// return new AuthorizationDecision(true);
// }
//
// }

View File

@ -0,0 +1 @@
如果需要重写验证逻辑(自定义)使用这里面的类,并在配置类`AuthorizationManagerConfiguration`解开注释,

View File

@ -0,0 +1,52 @@
package com.spring.step3.security.password;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import java.util.HexFormat;
/**
* <h1>MD5密码编码器实现</h1>
*
* <strong>安全警告</strong>此类使用MD5算法进行密码哈希已不再安全不推荐用于生产环境
*
* <p>MD5算法因其计算速度快且易受彩虹表攻击而被认为不安全即使密码哈希本身是单向的
* 但现代计算能力使得暴力破解和预先计算的彩虹表攻击变得可行</p>
*
* <p>Spring Security推荐使用BCryptPBKDF2Argon2或Scrypt等自适应单向函数替代MD5</p>
*
* @see PasswordEncoder
* 一般仅用于遗留系统兼容新系统应使用更安全的密码编码器
*/
public class MD5PasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("原始密码不能为null");
}
byte[] md5Digest = DigestUtils.md5Digest(rawPassword.toString().getBytes());
return HexFormat.of().formatHex(md5Digest);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("原始密码不能为null");
}
if (!StringUtils.hasText(encodedPassword)) {
return false;
}
return encodedPassword.equalsIgnoreCase(encode(rawPassword));
}
@Override
public boolean upgradeEncoding(String encodedPassword) {
// MD5已不安全始终返回true建议升级到更安全的算法
return true;
}
}

View File

@ -0,0 +1,80 @@
package com.spring.step3.security.service;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.spring.step3.domain.entity.PermissionEntity;
import com.spring.step3.domain.entity.RoleEntity;
import com.spring.step3.domain.entity.UserEntity;
import com.spring.step3.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@DS("testJwt")
@Service
@Transactional
@RequiredArgsConstructor
public class DbUserDetailService implements UserDetailsService {
private final UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 查询当前用户
UserEntity userEntity = userMapper.selectByUsername(username);
// 判断当前用户是否存在
if (userEntity == null) {
throw new UsernameNotFoundException("用户不存在");
}
Long userId = userEntity.getId();
List<String> list = new ArrayList<>();
// 设置用户角色
List<String> roles = findUserRolesByUserId(userId);
// 设置用户权限
List<String> permissions = findPermissionByUserId(userId);
list.addAll(roles);
list.addAll(permissions);
Set<SimpleGrantedAuthority> authorities = list.stream().map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet());
// 设置用户权限
userEntity.setAuthorities(authorities);
// 返回时将用户密码置为空
userEntity.setPassword(null);
return userEntity;
}
/**
* 根据用户id查找该用户的角色内容
*
* @param userId 用户id
* @return 当前用户的角色信息
*/
public List<String> findUserRolesByUserId(Long userId) {
List<RoleEntity> roleList = userMapper.selectRolesByUserId(userId);
return roleList.stream().map(RoleEntity::getRoleCode).toList();
}
/**
* 根据用户id查找该用户的权限内容
*
* @param userId 用户id
* @return 当前用户的权限信息
*/
public List<String> findPermissionByUserId(Long userId) {
List<PermissionEntity> permissionList = userMapper.selectPermissionByUserId(userId);
return permissionList.stream().map(PermissionEntity::getPermissionCode).toList();
}
}

View File

@ -0,0 +1,27 @@
package com.spring.step3.security.service;
// @Service
// @RequiredArgsConstructor
// public class InMemoryUserDetailsService implements UserDetailsService {
//
// private final PasswordEncoder passwordEncoder;
//
// @Override
// public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// // 1. 这里应该根据username从数据库或其他存储中查询用户信息
// // 以下是模拟数据实际应用中应从数据库查询
//
// // 2. 如果用户不存在抛出UsernameNotFoundException
// if (!"bunny".equalsIgnoreCase(username)) {
// throw new UsernameNotFoundException("User not found: " + username);
// }
//
// // 3. 构建UserDetails对象返回
// return User.builder()
// .username(username) // 使用传入的用户名
// .password(passwordEncoder.encode("123456")) // 密码应该已经加密存储这里仅为示例
// .roles("USER") // 角色会自动添加ROLE_前缀
// .authorities("read", "write") // 添加具体权限
// .build();
// }
// }

View File

@ -1,6 +1,6 @@
package com.spring.step2.security.service;
package com.spring.step3.security.service;
import com.spring.step2.utils.JwtTokenUtil;
import com.spring.step3.utils.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

View File

@ -0,0 +1,58 @@
package com.spring.step3.service.roles;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.spring.step3.domain.dto.permission.PermissionDto;
import com.spring.step3.domain.entity.PermissionEntity;
import com.spring.step3.domain.vo.PermissionVo;
import com.spring.step3.domain.vo.result.PageResult;
import java.util.List;
/**
* <p>
* 系统权限表 服务类
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
public interface PermissionService extends IService<PermissionEntity> {
/**
* 分页查询系统权限表
*
* @return {@link PermissionVo}
*/
PageResult<PermissionVo> getPermissionPage(Page<PermissionEntity> pageParams, PermissionDto dto);
/**
* 添加系统权限表
*
* @param dto {@link PermissionDto} 添加表单
*/
void addPermission(PermissionDto dto);
/**
* 更新系统权限表
*
* @param dto {@link PermissionDto} 更新表单
*/
void updatePermission(PermissionDto dto);
/**
* 删除|批量删除系统权限表类型
*
* @param ids 删除id列表
*/
void deletePermission(List<Long> ids);
/**
* 获取所有的权限列表
*
* @return 所有权限列表
*/
List<PermissionVo> getAllPermission();
}

View File

@ -0,0 +1,67 @@
package com.spring.step3.service.roles;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.spring.step3.domain.dto.role.AssignRolePermissionDto;
import com.spring.step3.domain.dto.role.RolePermissionDto;
import com.spring.step3.domain.entity.RolePermissionEntity;
import com.spring.step3.domain.vo.RolePermissionVo;
import com.spring.step3.domain.vo.result.PageResult;
import jakarta.validation.Valid;
import java.util.List;
/**
* <p>
* 角色权限关联表 服务类
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
public interface RolePermissionService extends IService<RolePermissionEntity> {
/**
* 分页查询角色权限关联表
*
* @return {@link RolePermissionVo}
*/
PageResult<RolePermissionVo> getRolePermissionPage(Page<RolePermissionEntity> pageParams, RolePermissionDto dto);
/**
* 添加角色权限关联表
*
* @param dto {@link RolePermissionDto} 添加表单
*/
void addRolePermission(RolePermissionDto dto);
/**
* 更新角色权限关联表
*
* @param dto {@link RolePermissionDto} 更新表单
*/
void updateRolePermission(RolePermissionDto dto);
/**
* 删除|批量删除角色权限关联表类型
*
* @param ids 删除id列表
*/
void deleteRolePermission(List<Long> ids);
/**
* 根据角色id获取权限内容
*
* @param permissionId 权限id
* @return 角色权限列表
*/
List<RolePermissionVo> getRolePermissionById(Long permissionId);
/**
* 根据角色id分配权限
*
* @param dto 为角色分配权限 {@link AssignRolePermissionDto}
*/
void assignRolePermission(@Valid AssignRolePermissionDto dto);
}

View File

@ -0,0 +1,57 @@
package com.spring.step3.service.roles;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.spring.step3.domain.dto.role.RoleDto;
import com.spring.step3.domain.entity.RoleEntity;
import com.spring.step3.domain.vo.RoleVo;
import com.spring.step3.domain.vo.result.PageResult;
import java.util.List;
/**
* <p>
* 系统角色表 服务类
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
public interface RoleService extends IService<RoleEntity> {
/**
* 分页查询系统角色表
*
* @return {@link RoleVo}
*/
PageResult<RoleVo> getRolePage(Page<RoleEntity> pageParams, RoleDto dto);
/**
* 添加系统角色表
*
* @param dto {@link RoleDto} 添加表单
*/
void addRole(RoleDto dto);
/**
* 更新系统角色表
*
* @param dto {@link RoleDto} 更新表单
*/
void updateRole(RoleDto dto);
/**
* 删除|批量删除系统角色表类型
*
* @param ids 删除id列表
*/
void deleteRole(List<Long> ids);
/**
* 获取全部角色列表
*
* @return 角色列表
*/
List<RoleVo> getRoleList();
}

View File

@ -0,0 +1,102 @@
package com.spring.step3.service.roles.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spring.step3.domain.dto.permission.PermissionDto;
import com.spring.step3.domain.entity.PermissionEntity;
import com.spring.step3.domain.vo.PermissionVo;
import com.spring.step3.domain.vo.result.PageResult;
import com.spring.step3.mapper.PermissionMapper;
import com.spring.step3.service.roles.PermissionService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* <p>
* 系统权限表 服务实现类
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@DS("testJwt")
@Service
@Transactional
@RequiredArgsConstructor
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, PermissionEntity> implements PermissionService {
/**
* * 系统权限表 服务实现类
*
* @param pageParams 系统权限表分页查询page对象
* @param dto 系统权限表分页查询对象
* @return 查询分页系统权限表返回对象
*/
@Override
public PageResult<PermissionVo> getPermissionPage(Page<PermissionEntity> pageParams, PermissionDto dto) {
IPage<PermissionVo> page = baseMapper.selectListByPage(pageParams, dto);
return PageResult.<PermissionVo>builder()
.list(page.getRecords())
.pageNo(page.getCurrent())
.pageSize(page.getSize())
.pages(page.getPages())
.total(page.getTotal())
.build();
}
/**
* 添加系统权限表
*
* @param dto 系统权限表添加
*/
@Override
public void addPermission(PermissionDto dto) {
PermissionEntity permission = new PermissionEntity();
BeanUtils.copyProperties(dto, permission);
save(permission);
}
/**
* 更新系统权限表
*
* @param dto 系统权限表更新
*/
@Override
public void updatePermission(PermissionDto dto) {
PermissionEntity permission = new PermissionEntity();
BeanUtils.copyProperties(dto, permission);
updateById(permission);
}
/**
* 删除|批量删除系统权限表
*
* @param ids 删除id列表
*/
@Override
public void deletePermission(List<Long> ids) {
removeByIds(ids);
}
/**
* 获取所有的权限列表
*
* @return 所有权限列表
*/
@Override
public List<PermissionVo> getAllPermission() {
return list().stream().map(permissionEntity -> {
PermissionVo permissionVo = new PermissionVo();
BeanUtils.copyProperties(permissionEntity, permissionVo);
return permissionVo;
}).toList();
}
}

View File

@ -0,0 +1,128 @@
package com.spring.step3.service.roles.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spring.step3.domain.dto.role.AssignRolePermissionDto;
import com.spring.step3.domain.dto.role.RolePermissionDto;
import com.spring.step3.domain.entity.RolePermissionEntity;
import com.spring.step3.domain.vo.RolePermissionVo;
import com.spring.step3.domain.vo.result.PageResult;
import com.spring.step3.mapper.RolePermissionMapper;
import com.spring.step3.service.roles.RolePermissionService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* <p>
* 角色权限关联表 服务实现类
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@DS("testJwt")
@Service
@Transactional
@RequiredArgsConstructor
public class RolePermissionServiceImpl extends ServiceImpl<RolePermissionMapper, RolePermissionEntity> implements RolePermissionService {
/**
* * 角色权限关联表 服务实现类
*
* @param pageParams 角色权限关联表分页查询page对象
* @param dto 角色权限关联表分页查询对象
* @return 查询分页角色权限关联表返回对象
*/
@Override
public PageResult<RolePermissionVo> getRolePermissionPage(Page<RolePermissionEntity> pageParams, RolePermissionDto dto) {
IPage<RolePermissionVo> page = baseMapper.selectListByPage(pageParams, dto);
return PageResult.<RolePermissionVo>builder()
.list(page.getRecords())
.pageNo(page.getCurrent())
.pageSize(page.getSize())
.pages(page.getPages())
.total(page.getTotal())
.build();
}
/**
* 添加角色权限关联表
*
* @param dto 角色权限关联表添加
*/
@Override
public void addRolePermission(RolePermissionDto dto) {
RolePermissionEntity rolePermission = new RolePermissionEntity();
BeanUtils.copyProperties(dto, rolePermission);
save(rolePermission);
}
/**
* 更新角色权限关联表
*
* @param dto 角色权限关联表更新
*/
@Override
public void updateRolePermission(RolePermissionDto dto) {
RolePermissionEntity rolePermission = new RolePermissionEntity();
BeanUtils.copyProperties(dto, rolePermission);
updateById(rolePermission);
}
/**
* 删除|批量删除角色权限关联表
*
* @param ids 删除id列表
*/
@Override
public void deleteRolePermission(List<Long> ids) {
removeByIds(ids);
}
/**
* 根据角色id获取权限内容
*
* @param permissionId 权限id
* @return 角色权限列表
*/
@Override
public List<RolePermissionVo> getRolePermissionById(Long permissionId) {
List<RolePermissionEntity> list = baseMapper.selectListByPermissionId(permissionId);
return list.stream().map(rolePermissionEntity -> {
RolePermissionVo rolePermissionVo = new RolePermissionVo();
BeanUtils.copyProperties(rolePermissionEntity, rolePermissionVo);
return rolePermissionVo;
}).toList();
}
/**
* 根据角色id分配权限
*
* @param dto 为角色分配权限 {@link AssignRolePermissionDto}
*/
@Override
public void assignRolePermission(AssignRolePermissionDto dto) {
Long roleId = dto.getRoleId();
List<Long> permissionIds = dto.getPermissionIds();
// 先删除当前已经分配的角色权限内容
baseMapper.deleteByRoleId(roleId);
List<RolePermissionEntity> rolePermissionEntityList = permissionIds.stream().map(permissionId -> {
RolePermissionEntity rolePermissionEntity = new RolePermissionEntity();
rolePermissionEntity.setRoleId(roleId);
rolePermissionEntity.setPermissionId(permissionId);
return rolePermissionEntity;
}).toList();
saveBatch(rolePermissionEntityList);
}
}

View File

@ -0,0 +1,104 @@
package com.spring.step3.service.roles.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spring.step3.domain.dto.role.RoleDto;
import com.spring.step3.domain.entity.RoleEntity;
import com.spring.step3.domain.vo.RoleVo;
import com.spring.step3.domain.vo.result.PageResult;
import com.spring.step3.mapper.RoleMapper;
import com.spring.step3.service.roles.RoleService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* <p>
* 系统角色表 服务实现类
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@DS("testJwt")
@Service
@Transactional
@RequiredArgsConstructor
public class RoleServiceImpl extends ServiceImpl<RoleMapper, RoleEntity> implements RoleService {
/**
* * 系统角色表 服务实现类
*
* @param pageParams 系统角色表分页查询page对象
* @param dto 系统角色表分页查询对象
* @return 查询分页系统角色表返回对象
*/
@Override
public PageResult<RoleVo> getRolePage(Page<RoleEntity> pageParams, RoleDto dto) {
IPage<RoleVo> page = baseMapper.selectListByPage(pageParams, dto);
return PageResult.<RoleVo>builder()
.list(page.getRecords())
.pageNo(page.getCurrent())
.pageSize(page.getSize())
.pages(page.getPages())
.total(page.getTotal())
.build();
}
/**
* 添加系统角色表
*
* @param dto 系统角色表添加
*/
@Override
public void addRole(RoleDto dto) {
RoleEntity role = new RoleEntity();
BeanUtils.copyProperties(dto, role);
save(role);
}
/**
* 更新系统角色表
*
* @param dto 系统角色表更新
*/
@Override
public void updateRole(RoleDto dto) {
RoleEntity role = new RoleEntity();
BeanUtils.copyProperties(dto, role);
updateById(role);
}
/**
* 删除|批量删除系统角色表
*
* @param ids 删除id列表
*/
@Override
public void deleteRole(List<Long> ids) {
removeByIds(ids);
}
/**
* 获取全部角色列表
*
* @return 角色列表
*/
@Override
public List<RoleVo> getRoleList() {
return list().stream()
.map(roleEntity -> {
RoleVo roleVo = new RoleVo();
BeanUtils.copyProperties(roleEntity, roleVo);
return roleVo;
})
.toList();
}
}

View File

@ -1,9 +1,9 @@
package com.spring.step2.service.user;
package com.spring.step3.service.user;
import com.baomidou.mybatisplus.extension.service.IService;
import com.spring.step2.domain.dto.LoginDto;
import com.spring.step2.domain.entity.UserEntity;
import com.spring.step2.domain.vo.LoginVo;
import com.spring.step3.domain.dto.LoginDto;
import com.spring.step3.domain.entity.UserEntity;
import com.spring.step3.domain.vo.LoginVo;
public interface LoginService extends IService<UserEntity> {

View File

@ -0,0 +1,67 @@
package com.spring.step3.service.user;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.spring.step3.domain.dto.user.AssignUserRoleDto;
import com.spring.step3.domain.dto.user.UserRoleDto;
import com.spring.step3.domain.entity.UserRoleEntity;
import com.spring.step3.domain.vo.UserRoleVo;
import com.spring.step3.domain.vo.result.PageResult;
import jakarta.validation.Valid;
import java.util.List;
/**
* <p>
* 用户角色关联表 服务类
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
public interface UserRoleService extends IService<UserRoleEntity> {
/**
* 分页查询用户角色关联表
*
* @return {@link UserRoleVo}
*/
PageResult<UserRoleVo> getUserRolePage(Page<UserRoleEntity> pageParams, UserRoleDto dto);
/**
* 添加用户角色关联表
*
* @param dto {@link UserRoleDto} 添加表单
*/
void addUserRole(UserRoleDto dto);
/**
* 更新用户角色关联表
*
* @param dto {@link UserRoleDto} 更新表单
*/
void updateUserRole(UserRoleDto dto);
/**
* 删除|批量删除用户角色关联表类型
*
* @param ids 删除id列表
*/
void deleteUserRole(List<Long> ids);
/**
* 根据用户id获取当前用户角色列表
*
* @param userId 用户id
* @return 用户和角色列表
*/
List<UserRoleVo> getRoleListByUserId(Long userId);
/**
* 根据用户id分配用户角色
*
* @param dto 用户分配角色DTO {@link AssignUserRoleDto}
*/
void assignUserRole(@Valid AssignUserRoleDto dto);
}

View File

@ -0,0 +1,49 @@
package com.spring.step3.service.user;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.spring.step3.domain.dto.user.UserDto;
import com.spring.step3.domain.entity.UserEntity;
import com.spring.step3.domain.vo.UserVo;
import com.spring.step3.domain.vo.result.PageResult;
import java.util.List;
/**
* <p>
* 用户基本信息表 服务类
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
public interface UserService extends IService<UserEntity> {
/**
* 分页查询用户基本信息表
*
* @return {@link UserVo}
*/
PageResult<UserVo> getUserPage(Page<UserEntity> pageParams, UserDto dto);
/**
* 添加用户基本信息表
*
* @param dto {@link UserDto} 添加表单
*/
void addUser(UserDto dto);
/**
* 更新用户基本信息表
*
* @param dto {@link UserDto} 更新表单
*/
void updateUser(UserDto dto);
/**
* 删除|批量删除用户基本信息表类型
*
* @param ids 删除id列表
*/
void deleteUser(List<Long> ids);
}

View File

@ -1,19 +1,19 @@
package com.spring.step2.service.user.impl;
package com.spring.step3.service.user.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spring.step2.domain.dto.LoginDto;
import com.spring.step2.domain.entity.UserEntity;
import com.spring.step2.domain.enums.LoginEnums;
import com.spring.step2.domain.vo.LoginVo;
import com.spring.step2.domain.vo.result.ResultCodeEnum;
import com.spring.step2.mapper.UserMapper;
import com.spring.step2.security.service.DbUserDetailService;
import com.spring.step2.security.service.JwtBearTokenService;
import com.spring.step2.service.user.LoginService;
import com.spring.step2.service.user.strategy.DefaultLoginStrategy;
import com.spring.step2.service.user.strategy.LoginContext;
import com.spring.step2.service.user.strategy.LoginStrategy;
import com.spring.step3.domain.dto.LoginDto;
import com.spring.step3.domain.entity.UserEntity;
import com.spring.step3.domain.enums.LoginEnums;
import com.spring.step3.domain.vo.LoginVo;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.mapper.UserMapper;
import com.spring.step3.security.service.DbUserDetailService;
import com.spring.step3.security.service.JwtBearTokenService;
import com.spring.step3.service.user.LoginService;
import com.spring.step3.service.user.strategy.DefaultLoginStrategy;
import com.spring.step3.service.user.strategy.LoginContext;
import com.spring.step3.service.user.strategy.LoginStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

View File

@ -0,0 +1,128 @@
package com.spring.step3.service.user.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spring.step3.domain.dto.user.AssignUserRoleDto;
import com.spring.step3.domain.dto.user.UserRoleDto;
import com.spring.step3.domain.entity.UserRoleEntity;
import com.spring.step3.domain.vo.UserRoleVo;
import com.spring.step3.domain.vo.result.PageResult;
import com.spring.step3.mapper.UserRoleMapper;
import com.spring.step3.service.user.UserRoleService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* <p>
* 用户角色关联表 服务实现类
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@DS("testJwt")
@Service
@Transactional
@RequiredArgsConstructor
public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRoleEntity> implements UserRoleService {
/**
* * 用户角色关联表 服务实现类
*
* @param pageParams 用户角色关联表分页查询page对象
* @param dto 用户角色关联表分页查询对象
* @return 查询分页用户角色关联表返回对象
*/
@Override
public PageResult<UserRoleVo> getUserRolePage(Page<UserRoleEntity> pageParams, UserRoleDto dto) {
IPage<UserRoleVo> page = baseMapper.selectListByPage(pageParams, dto);
return PageResult.<UserRoleVo>builder()
.list(page.getRecords())
.pageNo(page.getCurrent())
.pageSize(page.getSize())
.pages(page.getPages())
.total(page.getTotal())
.build();
}
/**
* 添加用户角色关联表
*
* @param dto 用户角色关联表添加
*/
@Override
public void addUserRole(UserRoleDto dto) {
UserRoleEntity userRole = new UserRoleEntity();
BeanUtils.copyProperties(dto, userRole);
save(userRole);
}
/**
* 更新用户角色关联表
*
* @param dto 用户角色关联表更新
*/
@Override
public void updateUserRole(UserRoleDto dto) {
UserRoleEntity userRole = new UserRoleEntity();
BeanUtils.copyProperties(dto, userRole);
updateById(userRole);
}
/**
* 删除|批量删除用户角色关联表
*
* @param ids 删除id列表
*/
@Override
public void deleteUserRole(List<Long> ids) {
removeByIds(ids);
}
/**
* 根据用户id获取当前用户角色列表
*
* @param userId 用户id
* @return 用户和角色列表
*/
@Override
public List<UserRoleVo> getRoleListByUserId(Long userId) {
List<UserRoleEntity> userRoleEntityList = baseMapper.getRoleListByUserId(userId);
return userRoleEntityList.stream().map(userRoleEntity -> {
UserRoleVo userRoleVo = new UserRoleVo();
BeanUtils.copyProperties(userRoleEntity, userRoleVo);
return userRoleVo;
})
.toList();
}
/**
* 根据用户id分配用户角色
*
* @param dto 用户分配角色DTO {@link AssignUserRoleDto}
*/
@Override
public void assignUserRole(AssignUserRoleDto dto) {
Long userId = dto.getUserId();
List<Long> roleIds = dto.getRoleIds();
// 先删除已经分配的角色
baseMapper.deleteByUserId(userId);
// 为用户分配角色
List<UserRoleEntity> entityList = roleIds.stream().map(roleId -> {
UserRoleEntity userRoleEntity = new UserRoleEntity();
userRoleEntity.setUserId(userId);
userRoleEntity.setRoleId(roleId);
return userRoleEntity;
}).toList();
saveBatch(entityList);
}
}

View File

@ -0,0 +1,109 @@
package com.spring.step3.service.user.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spring.step3.domain.dto.user.UserDto;
import com.spring.step3.domain.entity.UserEntity;
import com.spring.step3.domain.vo.UserVo;
import com.spring.step3.domain.vo.result.PageResult;
import com.spring.step3.mapper.UserMapper;
import com.spring.step3.service.user.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* <p>
* 用户基本信息表 服务实现类
* </p>
*
* @author Bunny
* @since 2025-07-11 22:36:53
*/
@DS("testJwt")
@Service
@Transactional
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {
private final PasswordEncoder passwordEncoder;
/**
* * 用户基本信息表 服务实现类
*
* @param pageParams 用户基本信息表分页查询page对象
* @param dto 用户基本信息表分页查询对象
* @return 查询分页用户基本信息表返回对象
*/
@Override
public PageResult<UserVo> getUserPage(Page<UserEntity> pageParams, UserDto dto) {
IPage<UserVo> page = baseMapper.selectListByPage(pageParams, dto);
return PageResult.<UserVo>builder()
.list(page.getRecords())
.pageNo(page.getCurrent())
.pageSize(page.getSize())
.pages(page.getPages())
.total(page.getTotal())
.build();
}
/**
* 添加用户基本信息表
*
* @param dto 用户基本信息表添加
*/
@Override
public void addUser(UserDto dto) {
UserEntity user = new UserEntity();
BeanUtils.copyProperties(dto, user);
// 用户密码是否为空为空设置默认密码
String password = user.getPassword();
password = StringUtils.hasText(password) ? password : "123456";
// 设置用户密码
String encodePassword = passwordEncoder.encode(password);
user.setPassword(encodePassword);
save(user);
}
/**
* 更新用户基本信息表
*
* @param dto 用户基本信息表更新
*/
@Override
public void updateUser(UserDto dto) {
UserEntity user = new UserEntity();
BeanUtils.copyProperties(dto, user);
// 设置用户密码
String password = user.getPassword();
if (StringUtils.hasText(password)) {
String encodePassword = passwordEncoder.encode(password);
user.setPassword(encodePassword);
}
updateById(user);
}
/**
* 删除|批量删除用户基本信息表
*
* @param ids 删除id列表
*/
@Override
public void deleteUser(List<Long> ids) {
removeByIds(ids);
}
}

View File

@ -1,9 +1,9 @@
package com.spring.step2.service.user.strategy;
package com.spring.step3.service.user.strategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.spring.step2.domain.dto.LoginDto;
import com.spring.step2.domain.entity.UserEntity;
import com.spring.step2.mapper.UserMapper;
import com.spring.step3.domain.dto.LoginDto;
import com.spring.step3.domain.entity.UserEntity;
import com.spring.step3.mapper.UserMapper;
/**
* 使用用户名登录

View File

@ -1,7 +1,8 @@
package com.spring.step2.service.user.strategy;
package com.spring.step3.service.user.strategy;
import com.spring.step2.domain.dto.LoginDto;
import com.spring.step2.domain.entity.UserEntity;
import com.spring.step3.domain.dto.LoginDto;
import com.spring.step3.domain.entity.UserEntity;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.Map;

View File

@ -1,8 +1,7 @@
package com.spring.step2.service.user.strategy;
package com.spring.step3.service.user.strategy;
import com.spring.step2.domain.dto.LoginDto;
import com.spring.step2.domain.entity.UserEntity;
import com.spring.step3.domain.dto.LoginDto;
import com.spring.step3.domain.entity.UserEntity;
/**
* 登录策略

View File

@ -1,12 +1,10 @@
package com.spring.step2.utils;
package com.spring.step3.utils;
import com.spring.step2.domain.vo.result.ResultCodeEnum;
import com.spring.step2.exception.SecurityException;
import com.spring.step3.domain.vo.result.ResultCodeEnum;
import com.spring.step3.exception.AuthenticSecurityException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.micrometer.common.lang.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
@ -123,26 +121,27 @@ public class JwtTokenUtil {
*/
public static Map<String, Object> getMapByToken(String token, SecretKey key) {
try {
if (!StringUtils.hasText(token)) throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
if (!StringUtils.hasText(token))
throw new AuthenticSecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
// body 值转为map
return Jwts.parser().verifyWith(key).build().parseSignedClaims(token).getPayload();
} catch (Exception exception) {
throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
throw new AuthenticSecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
}
}
@Nullable
private static String getSubjectByTokenHandler(String token, SecretKey key) {
try {
if (!StringUtils.hasText(token)) throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
if (!StringUtils.hasText(token))
throw new AuthenticSecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
Jws<Claims> claimsJws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
Claims body = claimsJws.getPayload();
return body.getSubject();
} catch (Exception exception) {
throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
throw new AuthenticSecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
}
}
@ -164,14 +163,14 @@ public class JwtTokenUtil {
*/
public static Long getUserId(String token, SecretKey key) {
try {
if (!StringUtils.hasText(token)) throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
if (!StringUtils.hasText(token)) throw new AuthenticSecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
Jws<Claims> claimsJws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
Claims claims = claimsJws.getPayload();
return Long.valueOf(String.valueOf(claims.get("userId")));
} catch (Exception exception) {
throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
throw new AuthenticSecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
}
}
@ -189,27 +188,17 @@ public class JwtTokenUtil {
Claims claims = claimsJws.getPayload();
return (String) claims.get("username");
} catch (Exception exception) {
throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
throw new AuthenticSecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
}
}
/**
* 判断token是否过期
*
* @param token token
* @return 是否过期
*/
public static boolean isExpired(String token, SecretKey key) {
return isExpiredUtil(token, key);
}
/**
* 判断是否过期
*
* @param token token
* @return 是否过期
*/
private static boolean isExpiredUtil(String token, SecretKey key) {
public static boolean isExpired(String token, SecretKey key) {
try {
Jws<Claims> claimsJws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
Date expiration = claimsJws.getPayload().getExpiration();

View File

@ -0,0 +1,26 @@
package com.spring.step3.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.spring.step3.domain.vo.result.Result;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import java.io.IOException;
public class ResponseUtil {
public static void out(HttpServletResponse response, Result<Object> result) {
try {
ObjectMapper mapper = new ObjectMapper();
// 注册JavaTimeModule模块
mapper.registerModule(new JavaTimeModule());
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpStatus.OK.value());
mapper.writeValue(response.getWriter(), result);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,13 @@
bunny:
master:
host: rm-bp12z6hlv46vi6g8mro.mysql.rds.aliyuncs.com
port: 3306
database: bunny_test
username: bunny_test
password: "Test1234"
testJwt:
host: rm-bp12z6hlv46vi6g8mro.mysql.rds.aliyuncs.com
port: 3306
database: test_jwt
username: bunny_test
password: "Test1234"

View File

@ -0,0 +1,57 @@
server:
port: 8772
spring:
application:
name: spring-security
profiles:
active: dev
devtools:
livereload:
port: 0
datasource:
dynamic:
primary: master
datasource:
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${bunny.master.host}:${bunny.master.port}/${bunny.master.database}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
username: ${bunny.master.username}
password: ${bunny.master.password}
testJwt:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${bunny.testJwt.host}:${bunny.testJwt.port}/${bunny.testJwt.database}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
username: ${bunny.testJwt.username}
password: ${bunny.testJwt.password}
hikari:
maximum-pool-size: 20
connection-timeout: 30000
jackson:
date-format: yyyy-MM-dd HH:mm:ss
mybatis-plus:
mapper-locations: classpath:/mapper/*.xml
global-config:
db-config:
id-type: assign_id
logic-delete-value: 1
logic-not-delete-value: 0
logic-delete-field: isDeleted
logging:
level:
root: info
com.spring: debug
org.springframework.security: debug
jwtToken:
# 密钥
secret: aVeryLongAndSecureRandomStringWithAtLeast32Characters
# 主题
subject: SecurityBunny
# 过期事件 7天
expired: 604800

View File

@ -0,0 +1,11 @@
____ ____ _ _
| __ ) _ _ _ __ _ __ _ _ / ___|| |_ _ _ __| |_ _
| _ \| | | | '_ \| '_ \| | | | \___ \| __| | | |/ _` | | | |
| |_) | |_| | | | | | | | |_| | ___) | |_| |_| | (_| | |_| |
|____/ \__,_|_| |_|_| |_|\__, | |____/ \__|\__,_|\__,_|\__, |
|___/ |___/
Service Name${spring.application.name}
SpringBoot Version: ${spring-boot.version}${spring-boot.formatted-version}
Spring Active${spring.profiles.active}

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<contextName>logback</contextName>
<!-- 格式化 年--日 输出 -->
<timestamp key="datetime" datePattern="yyyy-MM-dd"/>
<!-- 引入Spring属性 -->
<springProperty name="APP_NAME" source="spring.application.name" defaultValue="application"/>
<!--编码-->
<property name="ENCODING" value="UTF-8"/>
<!-- 控制台日志 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 临界值过滤器 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<pattern>
%cyan([%thread %d{yyyy-MM-dd HH:mm:ss}]) %yellow(%-5level) %green(%logger{100}).%boldRed(%method)-%boldMagenta(%line)-%blue(%msg%n)
</pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!-- 文件日志 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/${APP_NAME}/${datetime}.log</file>
<append>true</append>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %thread %file:%line %logger %msg%n</pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!-- 让SpringBoot内部日志ERROR级别 减少日志输出 -->
<logger name="org.springframework" level="ERROR" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<!-- 让mybatis整合包日志ERROR 减少日志输出 -->
<logger name="org.mybatis" level="ERROR" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<!-- 让ibatis 日志ERROR 减少日志输出 -->
<logger name="org.apache.ibatis" level="ERROR" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<!-- 让 tomcat包打印日志 日志ERROR 减少日志输出 -->
<logger name="org.apache" level="ERROR" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<!-- 我们自己开发的程序为DEBUG -->
<logger name="cn.bunny" level="DEBUG" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<logger name="com.baomidou" level="ERROR" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<!-- 根日志记录器INFO级别 -->
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>

View File

@ -0,0 +1,50 @@
<?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.spring.step3.mapper.PermissionMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.spring.step3.domain.entity.PermissionEntity">
<id column="id" property="id"/>
<id column="permission_code" property="permissionCode"/>
<id column="url" property="url"/>
<id column="method" property="method"/>
<id column="description" property="description"/>
<id column="remark" property="remark"/>
<id column="create_time" property="createTime"/>
<id column="update_time" property="updateTime"/>
<id column="create_user" property="createUser"/>
<id column="update_user" property="updateUser"/>
<id column="is_deleted" property="isDeleted"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id,permission_code,url,method,description,remark,create_time,update_time,create_user,update_user,is_deleted
</sql>
<!-- 分页查询系统权限表内容 -->
<select id="selectListByPage" resultType="com.spring.step3.domain.vo.PermissionVo">
select
<include refid="Base_Column_List"/>
from t_permission
<where>
is_deleted = 0
<if test="dto.permissionCode != null and dto.permissionCode != ''">
and permission_code like CONCAT('%',#{dto.permissionCode},'%')
</if>
<if test="dto.url != null and dto.url != ''">
and url like CONCAT('%',#{dto.url},'%')
</if>
<if test="dto.method != null and dto.method != ''">
and method like CONCAT('%',#{dto.method},'%')
</if>
<if test="dto.description != null and dto.description != ''">
and description like CONCAT('%',#{dto.description},'%')
</if>
<if test="dto.remark != null and dto.remark != ''">
and remark like CONCAT('%',#{dto.remark},'%')
</if>
</where>
</select>
</mapper>

View File

@ -0,0 +1,46 @@
<?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.spring.step3.mapper.RoleMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.spring.step3.domain.entity.RoleEntity">
<id column="id" property="id"/>
<id column="role_name" property="roleCode"/>
<id column="description" property="description"/>
<id column="remark" property="remark"/>
<id column="create_time" property="createTime"/>
<id column="update_time" property="updateTime"/>
<id column="create_user" property="createUser"/>
<id column="update_user" property="updateUser"/>
<id column="is_deleted" property="isDeleted"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id,role_name,description,remark,create_time,update_time,create_user,update_user,is_deleted
</sql>
<!-- 分页查询系统角色表内容 -->
<select id="selectListByPage" resultType="com.spring.step3.domain.vo.RoleVo">
select
*
from t_role
<where>
is_deleted = 0
<if test="dto.id != null and dto.id != ''">
and id like CONCAT('%',#{dto.id},'%')
</if>
<if test="dto.roleCode != null and dto.roleCode != ''">
and role_code like CONCAT('%',#{dto.roleCode},'%')
</if>
<if test="dto.description != null and dto.description != ''">
and description like CONCAT('%',#{dto.description},'%')
</if>
<if test="dto.remark != null and dto.remark != ''">
and remark like CONCAT('%',#{dto.remark},'%')
</if>
</where>
</select>
</mapper>

View File

@ -0,0 +1,50 @@
<?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.spring.step3.mapper.RolePermissionMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.spring.step3.domain.entity.RolePermissionEntity">
<id column="id" property="id"/>
<id column="role_id" property="roleId"/>
<id column="permission_id" property="permissionId"/>
<id column="create_time" property="createTime"/>
<id column="update_time" property="updateTime"/>
<id column="create_user" property="createUser"/>
<id column="update_user" property="updateUser"/>
<id column="is_deleted" property="isDeleted"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id,role_id,permission_id,create_time,update_time,create_user,update_user,is_deleted
</sql>
<!-- 先删除当前已经分配的角色权限内容 -->
<delete id="deleteByRoleId">
delete
from t_role_permission
where role_id = #{roleId}
</delete>
<!-- 分页查询角色权限关联表内容 -->
<select id="selectListByPage" resultType="com.spring.step3.domain.vo.RolePermissionVo">
select
<include refid="Base_Column_List"/>
from t_role_permission
<where>
base.is_deleted = 0
</where>
</select>
<!-- 根据角色id获取权限内容 -->
<select id="selectListByPermissionId" resultType="com.spring.step3.domain.entity.RolePermissionEntity">
select *
from t_role_permission
<where>
<if test="permissionId != null">
permission_id = #{permissionId}
</if>
</where>
</select>
</mapper>

View File

@ -0,0 +1,72 @@
<?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.spring.step3.mapper.UserMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.spring.step3.domain.entity.UserEntity">
<id column="id" property="id"/>
<id column="username" property="username"/>
<id column="password" property="password"/>
<id column="email" property="email"/>
<id column="create_time" property="createTime"/>
<id column="update_time" property="updateTime"/>
<id column="create_user" property="createUser"/>
<id column="update_user" property="updateUser"/>
<id column="is_deleted" property="isDeleted"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id,username,password,email,create_time,update_time,create_user,update_user,is_deleted
</sql>
<!-- 分页查询用户内容 -->
<select id="selectListByPage" resultType="com.spring.step3.domain.vo.UserVo">
select
<include refid="Base_Column_List"/>
from t_user
<where>
is_deleted = 0
<if test="dto.username != null and dto.username != ''">
and username like CONCAT('%',#{dto.username},'%')
</if>
<if test="dto.email != null and dto.email != ''">
and email like CONCAT('%',#{dto.email},'%')
</if>
</where>
</select>
<!-- 根据用户id查找当前用户的权限 -->
<select id="selectPermissionByUserId" resultType="com.spring.step3.domain.entity.PermissionEntity">
SELECT DISTINCT p.*
FROM t_permission p
JOIN t_role_permission rp ON p.id = rp.permission_id
JOIN t_user_role ur ON rp.role_id = ur.role_id
WHERE ur.user_id = #{userId}
</select>
<!-- 根据用户名查询当前用户 -->
<select id="selectByUsername" resultType="com.spring.step3.domain.entity.UserEntity">
select
<include refid="Base_Column_List"/>
from t_user
<where>
<if test="username != null and username != null">
username = #{username}
</if>
</where>
</select>
<!-- 根据用户id查找该用户的角色内容 -->
<select id="selectRolesByUserId" resultType="com.spring.step3.domain.entity.RoleEntity">
SELECT tr.*
FROM t_user_role tur
JOIN t_role tr ON tur.role_id = tr.id
<where>
<if test="userId != null">
tur.user_id = #{userId}
</if>
</where>
</select>
</mapper>

Some files were not shown because too many files have changed in this diff Show More