Compare commits
6 Commits
f803241c78
...
02901efa33
Author | SHA1 | Date |
---|---|---|
|
02901efa33 | |
|
0c7110e1fc | |
|
a317acdb63 | |
|
075925687c | |
|
7b64fc4e82 | |
|
8575c5931a |
|
@ -1,5 +1,9 @@
|
|||
# Spring Security 6 入门指南
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> 打包:mvn clean package -DskipTests
|
||||
|
||||
整个数据库的表构建差不多是这样的,也是简化的,作为一个小demo讲解。
|
||||
|
||||

|
||||
|
@ -1358,6 +1362,16 @@ public class AuthenticationEvents {
|
|||
|
||||
## 实现JWT的认证
|
||||
|
||||
### 过滤器的介绍
|
||||
|
||||
过滤器添加分为:`addFilterBefore`、`addFilterAt`、`addFilter`、`addFilterAfter`。
|
||||
|
||||
这里不推荐使用`addFilter`,指定顺序不明确,如需指定顺序使用其余三个。如果不指定顺序会报下面的错。
|
||||
|
||||
```properties
|
||||
The Filter class com.spring.step3.security.filter.JwtAuthenticationFilter does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.
|
||||
```
|
||||
|
||||
### 生成JWT令牌
|
||||
|
||||
> [!TIP]
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<module>official</module>
|
||||
<module>step-1</module>
|
||||
<module>step-2</module>
|
||||
<module>step-3</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</parent>
|
||||
|
||||
<artifactId>step-2</artifactId>
|
||||
<description>学习步骤1,不前后端分离的项目</description>
|
||||
<description>学习步骤2,不前后端分离的项目</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>step-2</name>
|
||||
|
@ -29,5 +29,11 @@
|
|||
<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>
|
||||
|
|
|
@ -3,19 +3,26 @@ package com.spring.step2.config.web;
|
|||
import com.spring.step2.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;
|
||||
|
||||
/**
|
||||
* 这里和Security关系不大,数据库更新用户名会用以下,只是写下,没什么背的含义
|
||||
*/
|
||||
@Configuration
|
||||
public class ThreadLocalCleanupInterceptor implements HandlerInterceptor {
|
||||
|
||||
/**
|
||||
* 这个类用处并不是很大,Security也是有拦截器的(过滤器)
|
||||
*/
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) {
|
||||
BaseContext.removeUser();
|
||||
}
|
||||
}
|
|
@ -13,11 +13,18 @@ public class WebConfig implements WebMvcConfigurer {
|
|||
|
||||
private final ThreadLocalCleanupInterceptor threadLocalCleanupInterceptor;
|
||||
|
||||
/**
|
||||
* 因为实现了 ThreadLocalCleanupInterceptor
|
||||
* 要做就做到底,就在这里随便用了下
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(threadLocalCleanupInterceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 这里不涉及远程调用,只是复制时候懒得改了
|
||||
*/
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
|
|
|
@ -16,6 +16,11 @@ public class WebController {
|
|||
return "loginPage";
|
||||
}
|
||||
|
||||
@GetMapping("logout")
|
||||
public String logout() {
|
||||
return "logoutPage";
|
||||
}
|
||||
|
||||
@GetMapping("/user")
|
||||
public String showUserPage() {
|
||||
return "userPage";
|
||||
|
|
|
@ -4,6 +4,7 @@ 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.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -32,7 +33,7 @@ public class CheckController {
|
|||
if (principal instanceof UserDetails) {
|
||||
return (UserDetails) principal;
|
||||
} else {
|
||||
return null;
|
||||
return User.builder().username("未知").password("未知").build();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
package com.spring.step2.domain.dto;
|
||||
|
||||
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 = "LoginRequest", title = "LoginRequest登录参数", description = "登录请求参数")
|
||||
public class LoginRequest {
|
||||
public class LoginDto {
|
||||
|
||||
@Schema(name = "type", description = "登录类型")
|
||||
private String type = "default";
|
||||
|
||||
@Schema(name = "username", title = "用户名")
|
||||
private String username;
|
|
@ -4,13 +4,13 @@ import com.baomidou.mybatisplus.annotation.TableField;
|
|||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.spring.step2.domain.entity.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.*;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("t_user")
|
||||
@Schema(name = "User对象", title = "用户", description = "用户的实体类对象")
|
||||
public class UserEntity extends BaseEntity {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.spring.step2.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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.spring.step2.domain.vo;
|
||||
|
||||
import com.spring.step2.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;
|
||||
|
||||
}
|
|
@ -8,7 +8,7 @@ public class AuthorizationLogic {
|
|||
public boolean decide(String name) {
|
||||
System.out.println(name);
|
||||
// 直接使用name的实现
|
||||
return name.equalsIgnoreCase("user");
|
||||
return name.equalsIgnoreCase("我真帅");
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,6 @@ 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.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
|
@ -19,35 +18,35 @@ public class SecurityWebConfiguration {
|
|||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
|
||||
http.authorizeHttpRequests(authorizeRequests ->
|
||||
// 访问路径为 /api 时需要进行认证
|
||||
authorizeRequests
|
||||
// 忽略认证接口
|
||||
.requestMatchers("/api/security/**").permitAll()
|
||||
// 只认证 /api/** 下的所有接口
|
||||
.requestMatchers("/api/**").authenticated()
|
||||
// 其余请求都放行
|
||||
.anyRequest().permitAll()
|
||||
)
|
||||
// 使用默认的登录
|
||||
// .formLogin(Customizer.withDefaults())
|
||||
// 禁用表单登录
|
||||
// .formLogin(AbstractHttpConfigurer::disable)
|
||||
.formLogin(loginPage -> loginPage
|
||||
// 自定义登录页路径
|
||||
.loginPage("/login-page")
|
||||
.loginPage("/login")
|
||||
// 处理登录的URL(默认就是/login)
|
||||
.loginProcessingUrl("/login")
|
||||
// 登录成功跳转
|
||||
.defaultSuccessUrl("/")
|
||||
// 登录失败跳转
|
||||
.failureUrl("/login-page?error=true")
|
||||
.failureUrl("/login?error=true")
|
||||
.permitAll()
|
||||
)
|
||||
// 使用默认的登录
|
||||
// .formLogin(Customizer.withDefaults())
|
||||
// 禁用表单登录
|
||||
// .formLogin(AbstractHttpConfigurer::disable)
|
||||
.logout(logout -> logout
|
||||
.logoutSuccessUrl("/login-page?logout=true")
|
||||
.logoutSuccessUrl("/logout")
|
||||
.permitAll()
|
||||
)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.exceptionHandling(exception -> {
|
||||
// 请求未授权接口
|
||||
exception.authenticationEntryPoint(new SecurityAuthenticationEntryPoint());
|
||||
|
|
|
@ -23,30 +23,33 @@ public class AuthenticationEvents {
|
|||
*/
|
||||
@EventListener
|
||||
public void onFailure(AuthorizationDeniedEvent<MethodInvocation> failure) {
|
||||
// getSource 和 getObject意思一样,一种是传入泛型自动转换一种是要手动转换
|
||||
Object source = failure.getSource();
|
||||
try {
|
||||
// getSource 和 getObject意思一样,一种是传入泛型自动转换一种是要手动转换
|
||||
Object source = failure.getSource();
|
||||
|
||||
// 直接获取泛型对象
|
||||
MethodInvocation methodInvocation = failure.getObject();
|
||||
Method method = methodInvocation.getMethod();
|
||||
Object[] args = methodInvocation.getArguments();
|
||||
// 直接获取泛型对象
|
||||
MethodInvocation methodInvocation = failure.getObject();
|
||||
Method method = methodInvocation.getMethod();
|
||||
Object[] args = methodInvocation.getArguments();
|
||||
|
||||
log.warn("方法调用被拒绝: {}.{}, 参数: {}",
|
||||
method.getDeclaringClass().getSimpleName(),
|
||||
method.getName(),
|
||||
Arrays.toString(args));
|
||||
log.warn("方法调用被拒绝: {}.{}, 参数: {}",
|
||||
method.getDeclaringClass().getSimpleName(),
|
||||
method.getName(),
|
||||
Arrays.toString(args));
|
||||
|
||||
// 这里面的信息,和接口 /api/security/current-user 内容一样
|
||||
Authentication authentication = failure.getAuthentication().get();
|
||||
// 这里面的信息,和接口 /api/security/current-user 内容一样
|
||||
Authentication authentication = failure.getAuthentication().get();
|
||||
|
||||
AuthorizationDecision authorizationDecision = failure.getAuthorizationDecision();
|
||||
// ExpressionAuthorizationDecision [granted=false, expressionAttribute=hasAuthority('ADMIN')]
|
||||
System.out.println(authorizationDecision);
|
||||
AuthorizationDecision authorizationDecision = failure.getAuthorizationDecision();
|
||||
// ExpressionAuthorizationDecision [granted=false, expressionAttribute=hasAuthority('ADMIN')]
|
||||
System.out.println(authorizationDecision);
|
||||
|
||||
log.warn("授权失败 - 用户: {}, 权限: {}", authentication.getName(), authorizationDecision);
|
||||
log.warn("授权失败 - 用户: {}, 权限: {}", authentication.getName(), authorizationDecision);
|
||||
} catch (Exception e) {
|
||||
log.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 监听授权的内容
|
||||
* 如果要监听授权成功的内容,这个内容可能相当的多,毕竟正常情况授权成功的内容还是比较多的。
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package com.spring.step2.security.handler;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.spring.step2.domain.vo.result.Result;
|
||||
import com.spring.step2.domain.vo.result.ResultCodeEnum;
|
||||
import com.spring.step2.utils.ResponseUtil;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
@ -14,19 +14,13 @@ 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);
|
||||
|
||||
// 转成JSON格式
|
||||
Object json = JSON.toJSON(result);
|
||||
|
||||
// 返回响应
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().println(json);
|
||||
ResponseUtil.out(response, result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package com.spring.step2.security.handler;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.spring.step2.domain.vo.result.Result;
|
||||
import com.spring.step2.domain.vo.result.ResultCodeEnum;
|
||||
import com.spring.step2.utils.ResponseUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -20,12 +20,6 @@ public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoin
|
|||
|
||||
// 未认证---未登录
|
||||
Result<Object> result = Result.error(authException.getMessage(), ResultCodeEnum.LOGIN_AUTH);
|
||||
|
||||
// 将错误的请求转成JSON
|
||||
Object json = JSON.toJSON(result);
|
||||
|
||||
// 返回响应
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().println(json);
|
||||
ResponseUtil.out(response, result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import com.spring.step2.domain.entity.RoleEntity;
|
|||
import com.spring.step2.domain.entity.UserEntity;
|
||||
import com.spring.step2.mapper.UserMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
@ -14,7 +14,10 @@ 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
|
||||
|
@ -35,28 +38,25 @@ public class DbUserDetailService implements UserDetailsService {
|
|||
}
|
||||
|
||||
Long userId = userEntity.getId();
|
||||
String password = userEntity.getPassword();
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
// 设置用户角色
|
||||
String[] roles = findUserRolesByUserId(userId).toArray(String[]::new);
|
||||
|
||||
List<String> roles = findUserRolesByUserId(userId);
|
||||
// 设置用户权限
|
||||
List<String> permissionsByUserId = findPermissionByUserId(userId);
|
||||
String[] permissions = permissionsByUserId.toArray(String[]::new);
|
||||
List<String> permissions = findPermissionByUserId(userId);
|
||||
list.addAll(roles);
|
||||
list.addAll(permissions);
|
||||
|
||||
// 也可以转成下面的形式
|
||||
// List<String> permissions = permissionsByUserId.stream()
|
||||
// .map(SimpleGrantedAuthority::new)
|
||||
// .toList();
|
||||
|
||||
String[] authorities = ArrayUtils.addAll(roles, permissions);
|
||||
Set<SimpleGrantedAuthority> authorities = list.stream().map(SimpleGrantedAuthority::new)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 设置用户权限
|
||||
return User.builder()
|
||||
.username(userEntity.getUsername())
|
||||
.password(userEntity.getPassword())
|
||||
// 设置用户 authorities
|
||||
.username(username)
|
||||
.password(password)
|
||||
.authorities(authorities)
|
||||
.roles(roles)
|
||||
.roles(roles.toArray(String[]::new))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
package com.spring.step2.service;
|
||||
|
||||
public interface LoginService {
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package com.spring.step2.service.impl;
|
||||
|
||||
import com.spring.step2.service.LoginService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class LoginServiceImpl implements LoginService {
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.spring.step2.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.spring.step2.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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,11 +47,3 @@ logging:
|
|||
root: info
|
||||
com.spring: debug
|
||||
org.springframework.security: debug
|
||||
|
||||
jwtToken:
|
||||
# 密钥
|
||||
secret: aVeryLongAndSecureRandomStringWithAtLeast32Characters
|
||||
# 主题
|
||||
subject: SecurityBunny
|
||||
# 过期事件 7天
|
||||
expired: 604800
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh_CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
|
@ -141,13 +141,13 @@
|
|||
<!-- 错误消息显示 -->
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert" th:if="${param.error}">
|
||||
<i class="fas fa-exclamation-circle me-2"></i>
|
||||
<span th:text="#{login.error}">无效的用户名或密码</span>
|
||||
<span>无效的用户名或密码</span>
|
||||
<button aria-label="Close" class="btn-close" data-bs-dismiss="alert" type="button"></button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert" th:if="${param.logout}">
|
||||
<i class="fas fa-check-circle me-2"></i>
|
||||
<span th:text="#{login.logout}">您已成功登出</span>
|
||||
<span>您已成功登出</span>
|
||||
<button aria-label="Close" class="btn-close" data-bs-dismiss="alert" type="button"></button>
|
||||
</div>
|
||||
|
||||
|
@ -180,23 +180,20 @@
|
|||
<i class="fas fa-sign-in-alt me-2"></i> 登录
|
||||
</button>
|
||||
|
||||
<div class="text-center mb-3">
|
||||
<a class="text-decoration-none" th:href="@{/forgot-password}">忘记密码?</a>
|
||||
</div>
|
||||
|
||||
<div class="divider">
|
||||
<span class="divider-text">或</span>
|
||||
<span class="divider-text">自定义登录页</span>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<p class="text-muted">还没有账户? <a class="text-decoration-none" th:href="@{/register}">注册</a></p>
|
||||
<p class="text-muted">想看接口? <a class="text-decoration-none" target="_blank" th:href="@{/doc.html}">注册</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="footer-links">
|
||||
<a href="#">隐私政策</a>
|
||||
<a href="#">使用条款</a>
|
||||
<a href="#">帮助中心</a>
|
||||
<a href="https://www.baidu.com/duty/">隐私政策</a>
|
||||
<a href="https://www.baidu.com/">使用条款</a>
|
||||
<a href="https://ir.baidu.com/" target="_blank">帮助中心</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>退出登录</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>完成退出</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -1,56 +0,0 @@
|
|||
package com.spring.step2.security.config;
|
||||
|
||||
import com.spring.step2.security.service.DbUserDetailService;
|
||||
import com.spring.step2.security.service.JwtBearTokenService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@SpringBootTest
|
||||
class JwtBearTokenServiceTest {
|
||||
|
||||
private String token;
|
||||
|
||||
@Autowired
|
||||
private JwtBearTokenService jwtBearTokenService;
|
||||
|
||||
@Autowired
|
||||
private DbUserDetailService dbUserDetailService;
|
||||
|
||||
@Test
|
||||
void createToken() {
|
||||
long userId = 1944384432521744386L;
|
||||
List<String> roles = dbUserDetailService.findUserRolesByUserId(userId);
|
||||
List<String> permissions = dbUserDetailService.findPermissionByUserId(userId);
|
||||
|
||||
String token = jwtBearTokenService.createToken(userId,
|
||||
"Bunny",
|
||||
roles,
|
||||
permissions);
|
||||
this.token = token;
|
||||
System.out.println(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUsernameFromToken() {
|
||||
createToken();
|
||||
|
||||
String username = jwtBearTokenService.getUsernameFromToken(token);
|
||||
System.out.println(username);
|
||||
|
||||
Long userId = jwtBearTokenService.getUserIdFromToken(token);
|
||||
System.out.println(userId);
|
||||
|
||||
Map<String, Object> map = jwtBearTokenService.getMapByToken(token);
|
||||
|
||||
List<?> roles = (List<?>) map.get("roles");
|
||||
System.out.println(roles);
|
||||
|
||||
// 安全转换 permissions
|
||||
List<?> permissions = (List<?>) map.get("permissions");
|
||||
System.out.println(permissions);
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.spring.step3.controller;
|
||||
|
||||
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;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "登录接口")
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
@RequiredArgsConstructor
|
||||
public class LoginController {
|
||||
|
||||
private final LoginService loginService;
|
||||
|
||||
@Operation(summary = "用户登录", description = "用户登录")
|
||||
@PostMapping("login")
|
||||
public Result<LoginVo> login(@RequestBody LoginDto loginDto) {
|
||||
LoginVo vo = loginService.login(loginDto);
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
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.User;
|
||||
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 User.builder().username("未知").password("未知").build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
// }
|
||||
|
||||
}
|
|
@ -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
|
||||
* @Target({ElementType.METHOD, ElementType.TYPE})
|
||||
* @Retention(RetentionPolicy.RUNTIME)
|
||||
* @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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
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
|
||||
// 前端段分离不需要---禁用明文验证
|
||||
.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)
|
||||
;
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package com.spring.step3.security.filter;
|
||||
|
||||
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;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final JwtBearTokenService jwtBearTokenService;
|
||||
private final DbUserDetailService userDetailsService;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(@NotNull HttpServletRequest request,
|
||||
@NotNull HttpServletResponse response,
|
||||
@NotNull FilterChain filterChain) throws ServletException, IOException, AuthenticSecurityException {
|
||||
final String authHeader = request.getHeader("Authorization");
|
||||
|
||||
// 如果当前请求不包含验证Token直接返回
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
filterChain.doFilter(request, response);
|
||||
// throw new SecurityException(ResultCodeEnum.LOGIN_AUTH);
|
||||
return;
|
||||
}
|
||||
|
||||
// 当前请求的Token
|
||||
final String jwtToken = authHeader.substring(7);
|
||||
|
||||
// 检查当前Token是否过期
|
||||
if (jwtBearTokenService.isTokenValid(jwtToken)) {
|
||||
// TODO 抛出异常 Security 未处理
|
||||
throw new AuthenticSecurityException(ResultCodeEnum.AUTHENTICATION_EXPIRED);
|
||||
}
|
||||
|
||||
// 解析当前Token中的用户名
|
||||
final String username = jwtBearTokenService.getUsernameFromToken(jwtToken);
|
||||
final Long userId = jwtBearTokenService.getUserIdFromToken(jwtToken);
|
||||
|
||||
// 当前用户名存在,并且 Security上下文为空,设置认证相关信息
|
||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
// 调用用户信息进行登录
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
||||
userDetails,
|
||||
null,
|
||||
userDetails.getAuthorities()
|
||||
);
|
||||
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
|
||||
// 设置认证用户信息
|
||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||
BaseContext.setUsername(username);
|
||||
BaseContext.setUserId(userId);
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
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.security.service.JwtBearTokenService;
|
||||
import com.spring.step3.utils.ResponseUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 实现注销处理器
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class JwtTokenLogoutHandler implements LogoutHandler {
|
||||
|
||||
private final JwtBearTokenService jwtBearTokenService;
|
||||
|
||||
@Override
|
||||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||
String authorizationToken = request.getHeader("Authorization");
|
||||
if (StringUtils.hasText(authorizationToken)) {
|
||||
// 如果当前用户信息存在redis中可以通过这个进行退出
|
||||
String username = jwtBearTokenService.getUsernameFromToken(authorizationToken);
|
||||
log.info("username : {}", username);
|
||||
}
|
||||
|
||||
Result<Object> result = Result.success(ResultCodeEnum.SUCCESS_LOGOUT);
|
||||
ResponseUtil.out(response, result);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 <MethodInvocation></code>
|
||||
// * </pre>
|
||||
// * 下面的这个是对方法执行后对权限的判断
|
||||
// * <pre>
|
||||
// * <code>AuthorizationManager <MethodInvocationResult></code>
|
||||
// * </pre>
|
||||
// *
|
||||
// * <h4>注意事项:</h4>
|
||||
// * 将上述两个方法按照自定义的方式进行实现后,还需要禁用默认的。
|
||||
// * <pre>
|
||||
// * @Configuration
|
||||
// * @EnableMethodSecurity(prePostEnabled = false)
|
||||
// * class MethodSecurityConfig {
|
||||
// * @Bean
|
||||
// * @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
// * Advisor preAuthorize(MyAuthorizationManager manager) {
|
||||
// * return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
|
||||
// * }
|
||||
// *
|
||||
// * @Bean
|
||||
// * @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);
|
||||
// }
|
||||
// }
|
|
@ -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 <MethodInvocation></code>
|
||||
// * </pre>
|
||||
// * 下面的这个是对方法执行后对权限的判断
|
||||
// * <pre>
|
||||
// * <code>AuthorizationManager <MethodInvocationResult></code>
|
||||
// * </pre>
|
||||
// *
|
||||
// * <h4>注意事项:</h4>
|
||||
// * 将上述两个方法按照自定义的方式进行实现后,还需要禁用默认的。
|
||||
// * <pre>
|
||||
// * @Configuration
|
||||
// * @EnableMethodSecurity(prePostEnabled = false)
|
||||
// * class MethodSecurityConfig {
|
||||
// * @Bean
|
||||
// * @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
// * Advisor preAuthorize(MyAuthorizationManager manager) {
|
||||
// * return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
|
||||
// * }
|
||||
// *
|
||||
// * @Bean
|
||||
// * @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);
|
||||
// }
|
||||
//
|
||||
// }
|
|
@ -0,0 +1 @@
|
|||
如果需要重写验证逻辑(自定义)使用这里面的类,并在配置类`AuthorizationManagerConfiguration`解开注释,
|
|
@ -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推荐使用BCrypt、PBKDF2、Argon2或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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
// }
|
||||
// }
|
|
@ -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;
|
||||
|
@ -83,4 +83,14 @@ public class JwtBearTokenService {
|
|||
return JwtTokenUtil.getMapByToken(token, secretKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前Token是否国企
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 是否国企
|
||||
*/
|
||||
public boolean isTokenValid(String token) {
|
||||
SecretKey secretKey = getSecretKey();
|
||||
return JwtTokenUtil.isExpired(token, secretKey);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue