初步实现jwt登录验证

This commit is contained in:
haotianmingyue 2025-07-07 11:34:51 +08:00
parent ddc5a9a7d0
commit 4fdb50e3b6
24 changed files with 2769 additions and 425 deletions

36
pom.xml
View File

@ -33,6 +33,7 @@
<springfox-swagger2>2.9.2</springfox-swagger2> <springfox-swagger2>2.9.2</springfox-swagger2>
<springfox-swagger-ui>2.9.2</springfox-swagger-ui> <springfox-swagger-ui>2.9.2</springfox-swagger-ui>
<springdoc-openapi-starter-webmvc-ui>2.5.0</springdoc-openapi-starter-webmvc-ui> <springdoc-openapi-starter-webmvc-ui>2.5.0</springdoc-openapi-starter-webmvc-ui>
<jjwt>0.11.5</jjwt>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
@ -69,6 +70,32 @@
</dependency> </dependency>
<!-- JJWT库 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt}</version>
<scope>runtime</scope>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Swagger 2 --> <!-- Swagger 2 -->
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>io.springfox</groupId>--> <!-- <groupId>io.springfox</groupId>-->
@ -79,6 +106,11 @@
<!-- <groupId>io.springfox</groupId>--> <!-- <groupId>io.springfox</groupId>-->
<!-- <artifactId>springfox-swagger-ui</artifactId>--> <!-- <artifactId>springfox-swagger-ui</artifactId>-->
<!-- <version>${springfox-swagger-ui}</version>--> <!-- <version>${springfox-swagger-ui}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-jpa</artifactId>-->
<!-- </dependency>--> <!-- </dependency>-->
<!-- 添加 springdoc --> <!-- 添加 springdoc -->
@ -87,6 +119,10 @@
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc-openapi-starter-webmvc-ui}</version> <version>${springdoc-openapi-starter-webmvc-ui}</version>
</dependency> </dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies> </dependencies>

View File

@ -0,0 +1,94 @@
package com.example.testspring.demos.modules.common.config;
import com.example.testspring.demos.modules.common.utils.JwtAuthEntryPoint;
import com.example.testspring.demos.modules.common.utils.JwtAuthenticationFilter;
//import com.example.testspring.demos.modules.user.service.impl.CustomUserDetailsService;
import com.example.testspring.demos.modules.user.service.impl.DatabaseUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtAuthEntryPoint unauthorizedHandler;
private final JwtAuthenticationFilter jwtAuthFilter;
private final DatabaseUserDetailsService userDetailsService;
@Autowired
public SecurityConfig(JwtAuthEntryPoint unauthorizedHandler,
JwtAuthenticationFilter jwtAuthFilter,
DatabaseUserDetailsService userDetailsService
) {
this.unauthorizedHandler = unauthorizedHandler;
this.jwtAuthFilter = jwtAuthFilter;
this.userDetailsService = userDetailsService;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// 禁用CSRF因使用JWT无需CSRF保护
.csrf(csrf -> csrf.disable())
// 异常处理配置
.exceptionHandling(handling -> handling
.authenticationEntryPoint(unauthorizedHandler)
)
// 设置会话无状态JWT不需要Session
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
// 授权规则配置
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/login").permitAll()
.requestMatchers("/test/**").permitAll()
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
);
// 添加JWT认证过滤器
http.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}

View File

@ -0,0 +1,22 @@
package com.example.testspring.demos.modules.common.entity;
import org.springframework.security.core.GrantedAuthority;
import java.io.Serializable;
import java.util.Collection;
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}

View File

@ -0,0 +1,21 @@
package com.example.testspring.demos.modules.common.exception;
public class UtilException extends RuntimeException
{
private static final long serialVersionUID = 8247610319171014183L;
public UtilException(Throwable e)
{
super(e.getMessage(), e);
}
public UtilException(String message)
{
super(message);
}
public UtilException(String message, Throwable throwable)
{
super(message, throwable);
}
}

View File

@ -0,0 +1,48 @@
package com.example.testspring.demos.modules.common.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 处理认证异常的统一入口点
*/
@Component
public class JwtAuthEntryPoint implements AuthenticationEntryPoint {
// private static final Logger logger = LoggerFactory.getLogger(JwtAuthEntryPoint.class);
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
// logger.error("未经授权的错误: {}", authException.getMessage());
// 构建标准化的错误响应
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("timestamp", System.currentTimeMillis());
errorDetails.put("status", HttpServletResponse.SC_UNAUTHORIZED);
errorDetails.put("error", "Unauthorized");
errorDetails.put("message", authException.getMessage());
errorDetails.put("path", request.getServletPath());
// 设置响应
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// 序列化错误信息
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(response.getOutputStream(), errorDetails);
}
}

View File

@ -0,0 +1,120 @@
package com.example.testspring.demos.modules.common.utils;
import com.example.testspring.demos.modules.user.entity.SysUser;
import com.example.testspring.demos.modules.user.service.impl.SysUserServiceImpl;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.security.SignatureException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
/**
* JWT 认证过滤器 - 每个请求都会经过此过滤器
*/
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
try {
// 1. 从请求头中提取JWT令牌
String jwtToken = parseJwt(request);
// 2. 如果令牌存在且有效
if (jwtToken != null && jwtUtil.validateToken(jwtToken)) {
// 3. 从令牌中提取用户名
String username = jwtUtil.getUsernameFromToken(jwtToken);
// 4. 加载用户详情
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 5. 创建认证令牌
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities());
// 6. 设置用户请求详情
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 7. 更新安全上下文
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (ExpiredJwtException ex) {
logger.error("JWT令牌已过期: {}", ex.getMessage());
request.setAttribute("expired", ex.getMessage());
} catch (SignatureException ex) {
logger.error("无效的JWT签名: {}", ex.getMessage());
request.setAttribute("invalid_signature", ex.getMessage());
} catch (Exception ex) {
logger.error("JWT认证失败: {}", ex.getMessage());
request.setAttribute("authentication_failure", ex.getMessage());
}
// 继续过滤器链
filterChain.doFilter(request, response);
}
/**
* 从请求头中提取JWT令牌
*/
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7);
}
// 尝试从cookie获取token备用
/*
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if ("JWT".equals(cookie.getName())) {
return cookie.getValue();
}
}
}
*/
return null;
}
/**
* 排除不需要JWT验证的请求可选
*/
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getServletPath();
// 白名单路径不需要JWT验证
return path.startsWith("/api/auth")
|| path.startsWith("/public/")
|| path.startsWith("/swagger")
|| path.startsWith("/v3/api-docs");
}
}

View File

@ -0,0 +1,20 @@
package com.example.testspring.demos.modules.common.utils;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Base64;
public class JwtKeyGenerator {
public static void main(String[] args) {
// 生成符合 HS512 安全要求的密钥
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
// 转换为 Base64 字符串以便存储
String base64Key = Base64.getEncoder().encodeToString(key.getEncoded());
System.out.println("Generated Base64-encoded secret key: " + base64Key);
System.out.println("Key length: " + (base64Key.length() * 6) + " bits (Base64 encoded)");
System.out.println("Actual key length: " + (key.getEncoded().length * 8) + " bits");
}
}

View File

@ -0,0 +1,144 @@
package com.example.testspring.demos.modules.common.utils;
import com.example.testspring.demos.modules.user.entity.LoginUser;
import com.example.testspring.demos.modules.user.entity.SysUser;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@Component
public class JwtUtil {
private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
@Value("${app.jwt.secret}")
private String jwtSecret;
@Value("${app.jwt.expiration-ms}")
private int jwtExpirationMs;
// @Value("${app.jwt.issuer}")
// private String jwtIssuer;
// 解析令牌
public String getUsernameFromToken(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date getExpirationDateFromToken(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
// 生成令牌
// public String generateToken(Authentication authentication) {
// UserDetails userDetails = (UserDetails) authentication.getPrincipal();
//
// Map<String, Object> claims = new HashMap<>();
// claims.put("roles", userDetails.getAuthorities().stream()
// .map(GrantedAuthority::getAuthority)
// .collect(Collectors.toList()));
// claims.put("iss", jwtIssuer);
// claims.put("jti", generateJti()); // 唯一标识符
//
// return createToken(claims, userDetails.getUsername());
// }
// 生成令牌
public String generateToken(LoginUser loginUser) {
String token = UUID.fastUUID().toString();
loginUser.setToken(token);
// setUserAgent(loginUser);
// refreshToken(loginUser);
Map<String, Object> claims = new HashMap<>();
claims.put("login_user_key", token);
return createToken(claims, loginUser.getUsername());
}
private String createToken(Map<String, Object> claims, String username) {
return Jwts.builder()
.setClaims(claims)
.setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + jwtExpirationMs))
// .signWith(getSigningKey(), SignatureAlgorithm.HS256)
// .signWith(Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret)), SignatureAlgorithm.HS512)
.signWith(getSigningKey(), SignatureAlgorithm.HS512)
.compact();
}
// 验证令牌
public boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token);
return true;
} catch (SignatureException e) {
logger.error("无效的JWT签名: {}", e.getMessage());
} catch (MalformedJwtException e) {
logger.error("无效的JWT令牌: {}", e.getMessage());
} catch (ExpiredJwtException e) {
logger.error("JWT令牌已过期: {}", e.getMessage());
} catch (UnsupportedJwtException e) {
logger.error("不支持的JWT令牌: {}", e.getMessage());
} catch (IllegalArgumentException e) {
logger.error("JWT声明字符串为空: {}", e.getMessage());
}
return false;
}
// 辅助方法
private Key getSigningKey() {
// 1. 将Base64编码的JWT密钥字符串解码为字节数组
byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
// 2. 使用字节数组生成HMAC-SHA算法所需的密钥
return Keys.hmacShaKeyFor(keyBytes);
}
// /**
// * 获取令牌的过期时间
// *
// * @param token JWT令牌
// * @return 过期时间
// */
// public long getExpirationTime(String token) {
// Date expiration = extractExpiration(token);
// return (expiration.getTime() - System.currentTimeMillis()) / 1000;
// }
private String generateJti() {
return java.util.UUID.randomUUID().toString();
}
}

View File

@ -0,0 +1,598 @@
package com.example.testspring.demos.modules.common.utils;
import org.springframework.util.AntPathMatcher;
import java.util.*;
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
/** 空字符串 */
private static final String NULLSTR = "";
/** 下划线 */
private static final char SEPARATOR = '_';
/**
* 获取参数不为空值
*
* @param value defaultValue 要判断的value
* @return value 返回值
*/
public static <T> T nvl(T value, T defaultValue)
{
return value != null ? value : defaultValue;
}
/**
* * 判断一个Collection是否为空 包含ListSetQueue
*
* @param coll 要判断的Collection
* @return true为空 false非空
*/
public static boolean isEmpty(Collection<?> coll)
{
return isNull(coll) || coll.isEmpty();
}
/**
* * 判断一个Collection是否非空包含ListSetQueue
*
* @param coll 要判断的Collection
* @return true非空 false
*/
public static boolean isNotEmpty(Collection<?> coll)
{
return !isEmpty(coll);
}
/**
* * 判断一个对象数组是否为空
*
* @param objects 要判断的对象数组
** @return true为空 false非空
*/
public static boolean isEmpty(Object[] objects)
{
return isNull(objects) || (objects.length == 0);
}
/**
* * 判断一个对象数组是否非空
*
* @param objects 要判断的对象数组
* @return true非空 false
*/
public static boolean isNotEmpty(Object[] objects)
{
return !isEmpty(objects);
}
/**
* * 判断一个Map是否为空
*
* @param map 要判断的Map
* @return true为空 false非空
*/
public static boolean isEmpty(Map<?, ?> map)
{
return isNull(map) || map.isEmpty();
}
/**
* * 判断一个Map是否为空
*
* @param map 要判断的Map
* @return true非空 false
*/
public static boolean isNotEmpty(Map<?, ?> map)
{
return !isEmpty(map);
}
/**
* * 判断一个字符串是否为空串
*
* @param str String
* @return true为空 false非空
*/
public static boolean isEmpty(String str)
{
return isNull(str) || NULLSTR.equals(str.trim());
}
/**
* * 判断一个字符串是否为非空串
*
* @param str String
* @return true非空串 false空串
*/
public static boolean isNotEmpty(String str)
{
return !isEmpty(str);
}
/**
* * 判断一个对象是否为空
*
* @param object Object
* @return true为空 false非空
*/
public static boolean isNull(Object object)
{
return object == null;
}
/**
* * 判断一个对象是否非空
*
* @param object Object
* @return true非空 false
*/
public static boolean isNotNull(Object object)
{
return !isNull(object);
}
/**
* * 判断一个对象是否是数组类型Java基本型别的数组
*
* @param object 对象
* @return true是数组 false不是数组
*/
public static boolean isArray(Object object)
{
return isNotNull(object) && object.getClass().isArray();
}
/**
* 去空格
*/
public static String trim(String str)
{
return (str == null ? "" : str.trim());
}
/**
* 截取字符串
*
* @param str 字符串
* @param start 开始
* @return 结果
*/
public static String substring(final String str, int start)
{
if (str == null)
{
return NULLSTR;
}
if (start < 0)
{
start = str.length() + start;
}
if (start < 0)
{
start = 0;
}
if (start > str.length())
{
return NULLSTR;
}
return str.substring(start);
}
/**
* 截取字符串
*
* @param str 字符串
* @param start 开始
* @param end 结束
* @return 结果
*/
public static String substring(final String str, int start, int end)
{
if (str == null)
{
return NULLSTR;
}
if (end < 0)
{
end = str.length() + end;
}
if (start < 0)
{
start = str.length() + start;
}
if (end > str.length())
{
end = str.length();
}
if (start > end)
{
return NULLSTR;
}
if (start < 0)
{
start = 0;
}
if (end < 0)
{
end = 0;
}
return str.substring(start, end);
}
/**
* 格式化文本, {} 表示占位符<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
* 如果想输出 {} 使用 \\转义 { 即可如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
* <br>
* 通常使用format("this is {} for {}", "a", "b") -> this is a for b<br>
* 转义{} format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
* 转义\ format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
*
* @param template 文本模板被替换的部分用 {} 表示
* @param params 参数值
* @return 格式化后的文本
*/
// public static String format(String template, Object... params)
// {
// if (isEmpty(params) || isEmpty(template))
// {
// return template;
// }
// return StrFormatter.format(template, params);
// }
/**
* 是否为http(s)://开头
*
* @param link 链接
* @return 结果
*/
// public static boolean ishttp(String link)
// {
// return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
// }
/**
* 字符串转set
*
* @param str 字符串
* @param sep 分隔符
* @return set集合
*/
public static final Set<String> str2Set(String str, String sep)
{
return new HashSet<String>(str2List(str, sep, true, false));
}
/**
* 字符串转list
*
* @param str 字符串
* @param sep 分隔符
* @param filterBlank 过滤纯空白
* @param trim 去掉首尾空白
* @return list集合
*/
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
{
List<String> list = new ArrayList<String>();
if (StringUtils.isEmpty(str))
{
return list;
}
// 过滤空白字符串
if (filterBlank && StringUtils.isBlank(str))
{
return list;
}
String[] split = str.split(sep);
for (String string : split)
{
if (filterBlank && StringUtils.isBlank(string))
{
continue;
}
if (trim)
{
string = string.trim();
}
list.add(string);
}
return list;
}
/**
* 判断给定的set列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
*
// * @param set 给定的集合
* @param array 给定的数组
* @return boolean 结果
*/
public static boolean containsAny(Collection<String> collection, String... array)
{
if (isEmpty(collection) || isEmpty(array))
{
return false;
}
else
{
for (String str : array)
{
if (collection.contains(str))
{
return true;
}
}
return false;
}
}
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
*
* @param cs 指定字符串
* @param searchCharSequences 需要检查的字符串数组
* @return 是否包含任意一个字符串
*/
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
{
if (isEmpty(cs) || isEmpty(searchCharSequences))
{
return false;
}
for (CharSequence testStr : searchCharSequences)
{
if (containsIgnoreCase(cs, testStr))
{
return true;
}
}
return false;
}
/**
* 驼峰转下划线命名
*/
public static String toUnderScoreCase(String str)
{
if (str == null)
{
return null;
}
StringBuilder sb = new StringBuilder();
// 前置字符是否大写
boolean preCharIsUpperCase = true;
// 当前字符是否大写
boolean curreCharIsUpperCase = true;
// 下一字符是否大写
boolean nexteCharIsUpperCase = true;
for (int i = 0; i < str.length(); i++)
{
char c = str.charAt(i);
if (i > 0)
{
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
}
else
{
preCharIsUpperCase = false;
}
curreCharIsUpperCase = Character.isUpperCase(c);
if (i < (str.length() - 1))
{
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
}
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
{
sb.append(SEPARATOR);
}
else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
{
sb.append(SEPARATOR);
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
/**
* 是否包含字符串
*
* @param str 验证字符串
* @param strs 字符串组
* @return 包含返回true
*/
public static boolean inStringIgnoreCase(String str, String... strs)
{
if (str != null && strs != null)
{
for (String s : strs)
{
if (str.equalsIgnoreCase(trim(s)))
{
return true;
}
}
}
return false;
}
/**
* 将下划线大写方式命名的字符串转换为驼峰式如果转换前的下划线大写方式命名的字符串为空则返回空字符串 例如HELLO_WORLD->HelloWorld
*
* @param name 转换前的下划线大写方式命名的字符串
* @return 转换后的驼峰式命名的字符串
*/
public static String convertToCamelCase(String name)
{
StringBuilder result = new StringBuilder();
// 快速检查
if (name == null || name.isEmpty())
{
// 没必要转换
return "";
}
else if (!name.contains("_"))
{
// 不含下划线仅将首字母大写
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
// 用下划线将原始字符串分割
String[] camels = name.split("_");
for (String camel : camels)
{
// 跳过原始字符串中开头结尾的下换线或双重下划线
if (camel.isEmpty())
{
continue;
}
// 首字母大写
result.append(camel.substring(0, 1).toUpperCase());
result.append(camel.substring(1).toLowerCase());
}
return result.toString();
}
/**
* 驼峰式命名法 例如user_name->userName
*/
public static String toCamelCase(String s)
{
if (s == null)
{
return null;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if (c == SEPARATOR)
{
upperCase = true;
}
else if (upperCase)
{
sb.append(Character.toUpperCase(c));
upperCase = false;
}
else
{
sb.append(c);
}
}
return sb.toString();
}
/**
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
*
* @param str 指定字符串
* @param strs 需要检查的字符串数组
* @return 是否匹配
*/
public static boolean matches(String str, List<String> strs)
{
if (isEmpty(str) || isEmpty(strs))
{
return false;
}
for (String pattern : strs)
{
if (isMatch(pattern, str))
{
return true;
}
}
return false;
}
/**
* 判断url是否与规则配置:
* ? 表示单个字符;
* * 表示一层路径内的任意字符串不可跨层级;
* ** 表示任意层路径;
*
* @param pattern 匹配规则
* @param url 需要匹配的url
* @return
*/
public static boolean isMatch(String pattern, String url)
{
AntPathMatcher matcher = new AntPathMatcher();
return matcher.match(pattern, url);
}
@SuppressWarnings("unchecked")
public static <T> T cast(Object obj)
{
return (T) obj;
}
/**
* 数字左边补齐0使之达到指定长度注意如果数字转换为字符串后长度大于size则只保留 最后size个字符
*
* @param num 数字对象
* @param size 字符串指定长度
* @return 返回数字的字符串格式该字符串为指定长度
*/
public static final String padl(final Number num, final int size)
{
return padl(num.toString(), size, '0');
}
/**
* 字符串左补齐如果原始字符串s长度大于size则只保留最后size个字符
*
* @param s 原始字符串
* @param size 字符串指定长度
* @param c 用于补齐的字符
* @return 返回指定长度的字符串由原字符串左补齐或截取得到
*/
public static final String padl(final String s, final int size, final char c)
{
final StringBuilder sb = new StringBuilder(size);
if (s != null)
{
final int len = s.length();
if (s.length() <= size)
{
for (int i = size - len; i > 0; i--)
{
sb.append(c);
}
sb.append(s);
}
else
{
return s.substring(len - size, len);
}
}
else
{
for (int i = size; i > 0; i--)
{
sb.append(c);
}
}
return sb.toString();
}
}

View File

@ -0,0 +1,481 @@
package com.example.testspring.demos.modules.common.utils;
import com.example.testspring.demos.modules.common.exception.UtilException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public final class UUID implements java.io.Serializable, Comparable<UUID>
{
private static final long serialVersionUID = -1185015143654744140L;
/**
* SecureRandom 的单例
*
*/
private static class Holder
{
static final SecureRandom numberGenerator = getSecureRandom();
}
/** 此UUID的最高64有效位 */
private final long mostSigBits;
/** 此UUID的最低64有效位 */
private final long leastSigBits;
/**
* 私有构造
*
* @param data 数据
*/
private UUID(byte[] data)
{
long msb = 0;
long lsb = 0;
assert data.length == 16 : "data must be 16 bytes in length";
for (int i = 0; i < 8; i++)
{
msb = (msb << 8) | (data[i] & 0xff);
}
for (int i = 8; i < 16; i++)
{
lsb = (lsb << 8) | (data[i] & 0xff);
}
this.mostSigBits = msb;
this.leastSigBits = lsb;
}
/**
* 使用指定的数据构造新的 UUID
*
* @param mostSigBits 用于 {@code UUID} 的最高有效 64
* @param leastSigBits 用于 {@code UUID} 的最低有效 64
*/
public UUID(long mostSigBits, long leastSigBits)
{
this.mostSigBits = mostSigBits;
this.leastSigBits = leastSigBits;
}
/**
* 获取类型 4伪随机生成的UUID 的静态工厂 使用加密的本地线程伪随机数生成器生成该 UUID
*
* @return 随机生成的 {@code UUID}
*/
public static UUID fastUUID()
{
return randomUUID(false);
}
/**
* 获取类型 4伪随机生成的UUID 的静态工厂 使用加密的强伪随机数生成器生成该 UUID
*
* @return 随机生成的 {@code UUID}
*/
public static UUID randomUUID()
{
return randomUUID(true);
}
/**
* 获取类型 4伪随机生成的UUID 的静态工厂 使用加密的强伪随机数生成器生成该 UUID
*
* @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码否则可以得到更好的性能
* @return 随机生成的 {@code UUID}
*/
public static UUID randomUUID(boolean isSecure)
{
final Random ng = isSecure ? Holder.numberGenerator : getRandom();
byte[] randomBytes = new byte[16];
ng.nextBytes(randomBytes);
randomBytes[6] &= 0x0f; /* clear version */
randomBytes[6] |= 0x40; /* set to version 4 */
randomBytes[8] &= 0x3f; /* clear variant */
randomBytes[8] |= 0x80; /* set to IETF variant */
return new UUID(randomBytes);
}
/**
* 根据指定的字节数组获取类型 3基于名称的UUID 的静态工厂
*
* @param name 用于构造 UUID 的字节数组
*
* @return 根据指定数组生成的 {@code UUID}
*/
public static UUID nameUUIDFromBytes(byte[] name)
{
MessageDigest md;
try
{
md = MessageDigest.getInstance("MD5");
}
catch (NoSuchAlgorithmException nsae)
{
throw new InternalError("MD5 not supported");
}
byte[] md5Bytes = md.digest(name);
md5Bytes[6] &= 0x0f; /* clear version */
md5Bytes[6] |= 0x30; /* set to version 3 */
md5Bytes[8] &= 0x3f; /* clear variant */
md5Bytes[8] |= 0x80; /* set to IETF variant */
return new UUID(md5Bytes);
}
/**
* 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}
*
* @param name 指定 {@code UUID} 字符串
* @return 具有指定值的 {@code UUID}
* @throws IllegalArgumentException 如果 name {@link #toString} 中描述的字符串表示形式不符抛出此异常
*
*/
public static UUID fromString(String name)
{
String[] components = name.split("-");
if (components.length != 5)
{
throw new IllegalArgumentException("Invalid UUID string: " + name);
}
for (int i = 0; i < 5; i++)
{
components[i] = "0x" + components[i];
}
long mostSigBits = Long.decode(components[0]).longValue();
mostSigBits <<= 16;
mostSigBits |= Long.decode(components[1]).longValue();
mostSigBits <<= 16;
mostSigBits |= Long.decode(components[2]).longValue();
long leastSigBits = Long.decode(components[3]).longValue();
leastSigBits <<= 48;
leastSigBits |= Long.decode(components[4]).longValue();
return new UUID(mostSigBits, leastSigBits);
}
/**
* 返回此 UUID 128 位值中的最低有效 64
*
* @return UUID 128 位值中的最低有效 64
*/
public long getLeastSignificantBits()
{
return leastSigBits;
}
/**
* 返回此 UUID 128 位值中的最高有效 64
*
* @return UUID 128 位值中最高有效 64
*/
public long getMostSignificantBits()
{
return mostSigBits;
}
/**
* 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的
* <p>
* 版本号具有以下含意:
* <ul>
* <li>1 基于时间的 UUID
* <li>2 DCE 安全 UUID
* <li>3 基于名称的 UUID
* <li>4 随机生成的 UUID
* </ul>
*
* @return {@code UUID} 的版本号
*/
public int version()
{
// Version is bits masked by 0x000000000000F000 in MS long
return (int) ((mostSigBits >> 12) & 0x0f);
}
/**
* 与此 {@code UUID} 相关联的变体号变体号描述 {@code UUID} 的布局
* <p>
* 变体号具有以下含意
* <ul>
* <li>0 NCS 向后兼容保留
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF&nbsp;RFC&nbsp;4122</a>(Leach-Salz), 用于此类
* <li>6 保留微软向后兼容
* <li>7 保留供以后定义使用
* </ul>
*
* @return {@code UUID} 相关联的变体号
*/
public int variant()
{
// This field is composed of a varying number of bits.
// 0 - - Reserved for NCS backward compatibility
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
// 1 1 0 Reserved, Microsoft backward compatibility
// 1 1 1 Reserved for future definition.
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
}
/**
* 与此 UUID 相关联的时间戳值
*
* <p>
* 60 位的时间戳值根据此 {@code UUID} time_lowtime_mid time_hi 字段构造<br>
* 所得到的时间戳以 100 毫微秒为单位 UTC通用协调时间 1582 10 15 日零时开始
*
* <p>
* 时间戳值仅在在基于时间的 UUID version 类型为 1中才有意义<br>
* 如果此 {@code UUID} 不是基于时间的 UUID则此方法抛出 UnsupportedOperationException
*
* @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 1 UUID
*/
public long timestamp() throws UnsupportedOperationException
{
checkTimeBase();
return (mostSigBits & 0x0FFFL) << 48//
| ((mostSigBits >> 16) & 0x0FFFFL) << 32//
| mostSigBits >>> 32;
}
/**
* 与此 UUID 相关联的时钟序列值
*
* <p>
* 14 位的时钟序列值根据此 UUID clock_seq 字段构造clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性
* <p>
* {@code clockSequence} 值仅在基于时间的 UUID version 类型为 1中才有意义 如果此 UUID 不是基于时间的 UUID则此方法抛出
* UnsupportedOperationException
*
* @return {@code UUID} 的时钟序列
*
* @throws UnsupportedOperationException 如果此 UUID version 不为 1
*/
public int clockSequence() throws UnsupportedOperationException
{
checkTimeBase();
return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
}
/**
* 与此 UUID 相关的节点值
*
* <p>
* 48 位的节点值根据此 UUID node 字段构造此字段旨在用于保存机器的 IEEE 802 地址该地址用于生成此 UUID 以保证空间唯一性
* <p>
* 节点值仅在基于时间的 UUID version 类型为 1中才有意义<br>
* 如果此 UUID 不是基于时间的 UUID则此方法抛出 UnsupportedOperationException
*
* @return {@code UUID} 的节点值
*
* @throws UnsupportedOperationException 如果此 UUID version 不为 1
*/
public long node() throws UnsupportedOperationException
{
checkTimeBase();
return leastSigBits & 0x0000FFFFFFFFFFFFL;
}
/**
* 返回此{@code UUID} 的字符串表现形式
*
* <p>
* UUID 的字符串表示形式由此 BNF 描述
*
* <pre>
* {@code
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
* time_low = 4*<hexOctet>
* time_mid = 2*<hexOctet>
* time_high_and_version = 2*<hexOctet>
* variant_and_sequence = 2*<hexOctet>
* node = 6*<hexOctet>
* hexOctet = <hexDigit><hexDigit>
* hexDigit = [0-9a-fA-F]
* }
* </pre>
*
* </blockquote>
*
* @return {@code UUID} 的字符串表现形式
* @see #toString(boolean)
*/
@Override
public String toString()
{
return toString(false);
}
/**
* 返回此{@code UUID} 的字符串表现形式
*
* <p>
* UUID 的字符串表示形式由此 BNF 描述
*
* <pre>
* {@code
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
* time_low = 4*<hexOctet>
* time_mid = 2*<hexOctet>
* time_high_and_version = 2*<hexOctet>
* variant_and_sequence = 2*<hexOctet>
* node = 6*<hexOctet>
* hexOctet = <hexDigit><hexDigit>
* hexDigit = [0-9a-fA-F]
* }
* </pre>
*
* </blockquote>
*
* @param isSimple 是否简单模式简单模式为不带'-'的UUID字符串
* @return {@code UUID} 的字符串表现形式
*/
public String toString(boolean isSimple)
{
final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
// time_low
builder.append(digits(mostSigBits >> 32, 8));
if (!isSimple)
{
builder.append('-');
}
// time_mid
builder.append(digits(mostSigBits >> 16, 4));
if (!isSimple)
{
builder.append('-');
}
// time_high_and_version
builder.append(digits(mostSigBits, 4));
if (!isSimple)
{
builder.append('-');
}
// variant_and_sequence
builder.append(digits(leastSigBits >> 48, 4));
if (!isSimple)
{
builder.append('-');
}
// node
builder.append(digits(leastSigBits, 12));
return builder.toString();
}
/**
* 返回此 UUID 的哈希码
*
* @return UUID 的哈希码值
*/
@Override
public int hashCode()
{
long hilo = mostSigBits ^ leastSigBits;
return ((int) (hilo >> 32)) ^ (int) hilo;
}
/**
* 将此对象与指定对象比较
* <p>
* 当且仅当参数不为 {@code null}而是一个 UUID 对象具有与此 UUID 相同的 varriant包含相同的值每一位均相同结果才为 {@code true}
*
* @param obj 要与之比较的对象
*
* @return 如果对象相同则返回 {@code true}否则返回 {@code false}
*/
@Override
public boolean equals(Object obj)
{
if ((null == obj) || (obj.getClass() != UUID.class))
{
return false;
}
UUID id = (UUID) obj;
return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
}
// Comparison Operations
/**
* 将此 UUID 与指定的 UUID 比较
*
* <p>
* 如果两个 UUID 不同且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段则第一个 UUID 大于第二个 UUID
*
* @param val 与此 UUID 比较的 UUID
*
* @return 在此 UUID 小于等于或大于 val 分别返回 -10 1
*
*/
@Override
public int compareTo(UUID val)
{
// The ordering is intentionally set up so that the UUIDs
// can simply be numerically compared as two numbers
return (this.mostSigBits < val.mostSigBits ? -1 : //
(this.mostSigBits > val.mostSigBits ? 1 : //
(this.leastSigBits < val.leastSigBits ? -1 : //
(this.leastSigBits > val.leastSigBits ? 1 : //
0))));
}
// -------------------------------------------------------------------------------------------------------------------
// Private method start
/**
* 返回指定数字对应的hex值
*
* @param val
* @param digits
* @return
*/
private static String digits(long val, int digits)
{
long hi = 1L << (digits * 4);
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
}
/**
* 检查是否为time-based版本UUID
*/
private void checkTimeBase()
{
if (version() != 1)
{
throw new UnsupportedOperationException("Not a time-based UUID");
}
}
/**
* 获取{@link SecureRandom}类提供加密的强随机数生成器 (RNG)
*
* @return {@link SecureRandom}
*/
public static SecureRandom getSecureRandom()
{
try
{
return SecureRandom.getInstance("SHA1PRNG");
}
catch (NoSuchAlgorithmException e)
{
throw new UtilException(e);
}
}
/**
* 获取随机数生成器对象<br>
* ThreadLocalRandom是JDK 7之后提供并发产生随机数能够解决多个线程发生的竞争争夺
*
* @return {@link ThreadLocalRandom}
*/
public static ThreadLocalRandom getRandom()
{
return ThreadLocalRandom.current();
}
}

View File

@ -0,0 +1,20 @@
package com.example.testspring.demos.modules.user.controller;
import com.example.testspring.demos.modules.common.utils.R;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/admin")
public class AdminController {
@GetMapping("/users")
@Parameter(name = "Authorization", description = "Authorization token", required = true, in = ParameterIn.HEADER)
public R getAllUsers() {
return R.ok("管理员用户列表");
}
}

View File

@ -0,0 +1,148 @@
package com.example.testspring.demos.modules.user.controller;
//import com.example.testspring.demos.modules.common.utils.JwtUtil;
import com.example.testspring.demos.modules.common.utils.JwtUtil;
import com.example.testspring.demos.modules.common.utils.R;
import com.example.testspring.demos.modules.user.entity.LoginUser;
import com.example.testspring.demos.modules.user.entity.dto.LoginDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
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;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/auth")
@Tag(name = "登录接口")
public class LoginController {
private final AuthenticationManager authenticationManager;
private final JwtUtil jwtUtil;
// private final UserRepository userRepository;
// private final LoginRateLimiter loginRateLimiter;
// private final LoginAuditService loginAuditService;
// 使用构造函数注入
@Autowired
public LoginController(AuthenticationManager authenticationManager,
JwtUtil jwtUtil
/* 如果需要添加以下参数
LoginRateLimiter loginRateLimiter,
LoginAuditService loginAuditService */
) {
this.authenticationManager = authenticationManager;
this.jwtUtil = jwtUtil;
// this.loginRateLimiter = loginRateLimiter;
// this.loginAuditService = loginAuditService;
}
@PostMapping("/login")
@Operation(summary = "用户登录")
public R authenticateUser(@RequestBody LoginDto loginRequest,
HttpServletRequest request) {
// 获取客户端IP地址
String clientIp = getClientIp(request);
System.out.println("登录请求:" + loginRequest);
System.out.println("客户端IP地址" + clientIp);
System.out.println("用户名:" + loginRequest.getUsername());
System.out.println("密码:" + loginRequest.getPassword());
// 检查是否允许登录限流
// if (!loginRateLimiter.allowLogin(loginRequest.getUsername())) {
// return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
// .body("登录尝试过多,请稍后再试");
// }
try {
// 创建认证令牌
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
// 设置认证信息到安全上下文
SecurityContextHolder.getContext().setAuthentication(authentication);
// User user = userRepository.findByUsername(loginRequest.getUsername())
// .orElseThrow(() -> new UsernameNotFoundException("用户未找到: " + loginRequest.getUsername()));
// // 获取用户详情
// UserDetails userDetails = (UserDetails) authentication.getPrincipal();
//
// // 提取用户角色
// List<String> roles = userDetails.getAuthorities().stream()
// .map(GrantedAuthority::getAuthority)
// .collect(Collectors.toList());
// 生成JWT令牌
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
String jwt = jwtUtil.generateToken(loginUser);
// 获取令牌有效期
// long expiresIn = jwtUtil.getExpirationTime(jwt);
// // 创建响应对象
// LoginResponseDTO response = new LoginResponseDTO(
// jwt,
// expiresIn,
// userDetails.getUsername(),
// roles
// );
//
// // 登录成功重置限流器
// loginRateLimiter.loginSuccess(loginRequest.getUsername());
//
// // 记录登录成功审计日志
// loginAuditService.logLoginSuccess(loginRequest.getUsername(), clientIp);
R response = R.ok("登录成功");
response.put("token", jwt);
return response;
} catch (AuthenticationException e) {
// 记录登录失败审计日志
// loginAuditService.logLoginFailure(loginRequest.getUsername(), clientIp);
return R.error(500,"用户名或密码错误");
}
}
// 获取客户端IP地址
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}

View File

@ -44,4 +44,10 @@ public class testController {
public R testR(){ public R testR(){
return R.ok().put("data",robotService.selectRobotList(new Robot())); return R.ok().put("data",robotService.selectRobotList(new Robot()));
} }
@GetMapping("/testUser")
@Operation(summary = "测试获取用户列表")
public R testUser(){
return R.ok().put("data",sysUserService.selectUserList(new SysUser()));
}
} }

View File

@ -0,0 +1,260 @@
package com.example.testspring.demos.modules.user.entity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
public class LoginUser implements UserDetails
{
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
private Long userId;
/**
* 部门ID
*/
private Long deptId;
/**
* 用户唯一标识
*/
private String token;
/**
* 登录时间
*/
private Long loginTime;
/**
* 过期时间
*/
private Long expireTime;
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 权限列表
*/
private Set<String> permissions;
/**
* 用户信息
*/
private SysUser user;
public Long getUserId()
{
return userId;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public Long getDeptId()
{
return deptId;
}
public void setDeptId(Long deptId)
{
this.deptId = deptId;
}
public String getToken()
{
return token;
}
public void setToken(String token)
{
this.token = token;
}
public LoginUser()
{
}
public LoginUser(SysUser user, Set<String> permissions)
{
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
{
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
// @JSONField(serialize = false)
@Override
public String getPassword()
{
return user.getPassword();
}
@Override
public String getUsername()
{
return user.getUserName();
}
/**
* 账户是否未过期,过期无法验证
*/
// @JSONField(serialize = false)
@Override
public boolean isAccountNonExpired()
{
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
*
* @return
*/
// @JSONField(serialize = false)
@Override
public boolean isAccountNonLocked()
{
return true;
}
/**
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
*
* @return
*/
// @JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired()
{
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*
* @return
*/
// @JSONField(serialize = false)
@Override
public boolean isEnabled()
{
return true;
}
public Long getLoginTime()
{
return loginTime;
}
public void setLoginTime(Long loginTime)
{
this.loginTime = loginTime;
}
public String getIpaddr()
{
return ipaddr;
}
public void setIpaddr(String ipaddr)
{
this.ipaddr = ipaddr;
}
public String getLoginLocation()
{
return loginLocation;
}
public void setLoginLocation(String loginLocation)
{
this.loginLocation = loginLocation;
}
public String getBrowser()
{
return browser;
}
public void setBrowser(String browser)
{
this.browser = browser;
}
public String getOs()
{
return os;
}
public void setOs(String os)
{
this.os = os;
}
public Long getExpireTime()
{
return expireTime;
}
public void setExpireTime(Long expireTime)
{
this.expireTime = expireTime;
}
public Set<String> getPermissions()
{
return permissions;
}
public void setPermissions(Set<String> permissions)
{
this.permissions = permissions;
}
public SysUser getUser()
{
return user;
}
public void setUser(SysUser user)
{
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
{
return null;
}
}

View File

@ -1,335 +1,297 @@
package com.example.testspring.demos.modules.user.entity; package com.example.testspring.demos.modules.user.entity;
import java.util.Date; import com.example.testspring.demos.modules.common.entity.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class SysUser { import java.util.Date;
/** import java.util.List;
*
* This field was generated by MyBatis Generator. public class SysUser extends BaseEntity
* This field corresponds to the database column sys_user.user_id {
* private static final long serialVersionUID = 1L;
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/ /** 用户ID */
private Long userId; private Long userId;
/** /** 部门ID */
* private Long deptId;
* This field was generated by MyBatis Generator.
* This field corresponds to the database column sys_user.username
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
private String username;
/** /** 用户账号 */
* private String userName;
* This field was generated by MyBatis Generator.
* This field corresponds to the database column sys_user.password
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
private String password;
/** /** 用户昵称 */
* private String nickName;
* This field was generated by MyBatis Generator.
* This field corresponds to the database column sys_user.name
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
private String name;
/** /** 用户邮箱 */
*
* This field was generated by MyBatis Generator.
* This field corresponds to the database column sys_user.salt
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
private String salt;
/**
*
* This field was generated by MyBatis Generator.
* This field corresponds to the database column sys_user.email
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
private String email; private String email;
/** /** 手机号码 */
* private String phonenumber;
* This field was generated by MyBatis Generator.
* This field corresponds to the database column sys_user.mobile
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
private String mobile;
/** /** 用户性别 */
* private String sex;
* This field was generated by MyBatis Generator.
* This field corresponds to the database column sys_user.status
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
private Byte status;
/** /** 用户头像 */
* private String avatar;
* This field was generated by MyBatis Generator.
* This field corresponds to the database column sys_user.create_user_id
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
private Long createUserId;
/** /** 密码 */
* private String password;
* This field was generated by MyBatis Generator.
* This field corresponds to the database column sys_user.create_time /** 帐号状态0正常 1停用 */
* private String status;
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/ /** 删除标志0代表存在 2代表删除 */
private Date createTime; private String delFlag;
/** 最后登录IP */
private String loginIp;
/** 最后登录时间 */
private Date loginDate;
/** 部门对象 */
// private SysDept dept;
/** 角色对象 */
// private List<SysRole> roles;
/** 角色组 */
private Long[] roleIds;
/** 岗位组 */
private Long[] postIds;
/** 角色ID */
private Long roleId;
public SysUser()
{
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column sys_user.user_id
*
* @return the value of sys_user.user_id
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public Long getUserId() {
return userId;
} }
/** public SysUser(Long userId)
* This method was generated by MyBatis Generator. {
* This method sets the value of the database column sys_user.user_id
*
* @param userId the value for sys_user.user_id
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public void setUserId(Long userId) {
this.userId = userId; this.userId = userId;
} }
/** public Long getUserId()
* This method was generated by MyBatis Generator. {
* This method returns the value of the database column sys_user.username return userId;
*
* @return the value of sys_user.username
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public String getUsername() {
return username;
} }
/** public void setUserId(Long userId)
* This method was generated by MyBatis Generator. {
* This method sets the value of the database column sys_user.username this.userId = userId;
*
* @param username the value for sys_user.username
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public void setUsername(String username) {
this.username = username;
} }
/** public boolean isAdmin()
* This method was generated by MyBatis Generator. {
* This method returns the value of the database column sys_user.password return isAdmin(this.userId);
*
* @return the value of sys_user.password
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public String getPassword() {
return password;
} }
/** public static boolean isAdmin(Long userId)
* This method was generated by MyBatis Generator. {
* This method sets the value of the database column sys_user.password return userId != null && 1L == userId;
*
* @param password the value for sys_user.password
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public void setPassword(String password) {
this.password = password;
} }
/** public Long getDeptId()
* This method was generated by MyBatis Generator. {
* This method returns the value of the database column sys_user.name return deptId;
*
* @return the value of sys_user.name
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public String getName() {
return name;
} }
/** public void setDeptId(Long deptId)
* This method was generated by MyBatis Generator. {
* This method sets the value of the database column sys_user.name this.deptId = deptId;
*
* @param name the value for sys_user.name
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public void setName(String name) {
this.name = name;
} }
/**
* This method was generated by MyBatis Generator. public String getNickName()
* This method returns the value of the database column sys_user.salt {
* return nickName;
* @return the value of sys_user.salt
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public String getSalt() {
return salt;
} }
/** public void setNickName(String nickName)
* This method was generated by MyBatis Generator. {
* This method sets the value of the database column sys_user.salt this.nickName = nickName;
*
* @param salt the value for sys_user.salt
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public void setSalt(String salt) {
this.salt = salt;
} }
/**
* This method was generated by MyBatis Generator. public String getUserName()
* This method returns the value of the database column sys_user.email {
* return userName;
* @return the value of sys_user.email }
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025 public void setUserName(String userName)
*/ {
public String getEmail() { this.userName = userName;
}
public String getEmail()
{
return email; return email;
} }
/** public void setEmail(String email)
* This method was generated by MyBatis Generator. {
* This method sets the value of the database column sys_user.email
*
* @param email the value for sys_user.email
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public void setEmail(String email) {
this.email = email; this.email = email;
} }
/**
* This method was generated by MyBatis Generator. public String getPhonenumber()
* This method returns the value of the database column sys_user.mobile {
* return phonenumber;
* @return the value of sys_user.mobile
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public String getMobile() {
return mobile;
} }
/** public void setPhonenumber(String phonenumber)
* This method was generated by MyBatis Generator. {
* This method sets the value of the database column sys_user.mobile this.phonenumber = phonenumber;
*
* @param mobile the value for sys_user.mobile
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public void setMobile(String mobile) {
this.mobile = mobile;
} }
/** public String getSex()
* This method was generated by MyBatis Generator. {
* This method returns the value of the database column sys_user.status return sex;
* }
* @return the value of sys_user.status
* public void setSex(String sex)
* @mbg.generated Thu Jun 26 17:58:24 CST 2025 {
*/ this.sex = sex;
public Byte getStatus() { }
public String getAvatar()
{
return avatar;
}
public void setAvatar(String avatar)
{
this.avatar = avatar;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getStatus()
{
return status; return status;
} }
/** public void setStatus(String status)
* This method was generated by MyBatis Generator. {
* This method sets the value of the database column sys_user.status
*
* @param status the value for sys_user.status
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public void setStatus(Byte status) {
this.status = status; this.status = status;
} }
/** public String getDelFlag()
* This method was generated by MyBatis Generator. {
* This method returns the value of the database column sys_user.create_user_id return delFlag;
*
* @return the value of sys_user.create_user_id
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public Long getCreateUserId() {
return createUserId;
} }
/** public void setDelFlag(String delFlag)
* This method was generated by MyBatis Generator. {
* This method sets the value of the database column sys_user.create_user_id this.delFlag = delFlag;
*
* @param createUserId the value for sys_user.create_user_id
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public void setCreateUserId(Long createUserId) {
this.createUserId = createUserId;
} }
/** public String getLoginIp()
* This method was generated by MyBatis Generator. {
* This method returns the value of the database column sys_user.create_time return loginIp;
*
* @return the value of sys_user.create_time
*
* @mbg.generated Thu Jun 26 17:58:24 CST 2025
*/
public Date getCreateTime() {
return createTime;
} }
/** public void setLoginIp(String loginIp)
* This method was generated by MyBatis Generator. {
* This method sets the value of the database column sys_user.create_time this.loginIp = loginIp;
* }
* @param createTime the value for sys_user.create_time
* public Date getLoginDate()
* @mbg.generated Thu Jun 26 17:58:24 CST 2025 {
*/ return loginDate;
public void setCreateTime(Date createTime) { }
this.createTime = createTime;
public void setLoginDate(Date loginDate)
{
this.loginDate = loginDate;
}
// public SysDept getDept()
// {
// return dept;
// }
//
// public void setDept(SysDept dept)
// {
// this.dept = dept;
// }
//
// public List<SysRole> getRoles()
// {
// return roles;
// }
//
// public void setRoles(List<SysRole> roles)
// {
// this.roles = roles;
// }
public Long[] getRoleIds()
{
return roleIds;
}
public void setRoleIds(Long[] roleIds)
{
this.roleIds = roleIds;
}
public Long[] getPostIds()
{
return postIds;
}
public void setPostIds(Long[] postIds)
{
this.postIds = postIds;
}
public Long getRoleId()
{
return roleId;
}
public void setRoleId(Long roleId)
{
this.roleId = roleId;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("userId", getUserId())
.append("deptId", getDeptId())
.append("userName", getUserName())
.append("nickName", getNickName())
.append("email", getEmail())
.append("phonenumber", getPhonenumber())
.append("sex", getSex())
.append("avatar", getAvatar())
.append("password", getPassword())
.append("status", getStatus())
.append("delFlag", getDelFlag())
.append("loginIp", getLoginIp())
.append("loginDate", getLoginDate())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
// .append("dept", getDept())
.toString();
} }
} }

View File

@ -0,0 +1,23 @@
package com.example.testspring.demos.modules.user.entity.dto;
public class LoginDto {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -1,52 +1,131 @@
package com.example.testspring.demos.modules.user.mapper; package com.example.testspring.demos.modules.user.mapper;
import com.example.testspring.demos.modules.user.entity.SysUser; import com.example.testspring.demos.modules.user.entity.SysUser;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
/**
* 用户表 数据层
*
* @author ruoyi
*/
@Mapper @Mapper
public interface SysUserMapper { public interface SysUserMapper
{
/** /**
* This method was generated by MyBatis Generator. * 根据条件分页查询用户列表
* This method corresponds to the database table sys_user
* *
* @mbg.generated Thu Jun 26 17:58:24 CST 2025 * @param sysUser 用户信息
* @return 用户信息集合信息
*/ */
int deleteByPrimaryKey(Long userId); public List<SysUser> selectUserList(SysUser sysUser);
/** /**
* This method was generated by MyBatis Generator. * 根据条件分页查询已配用户角色列表
* This method corresponds to the database table sys_user
* *
* @mbg.generated Thu Jun 26 17:58:24 CST 2025 * @param user 用户信息
* @return 用户信息集合信息
*/ */
int insert(SysUser record); public List<SysUser> selectAllocatedList(SysUser user);
/** /**
* This method was generated by MyBatis Generator. * 根据条件分页查询未分配用户角色列表
* This method corresponds to the database table sys_user
* *
* @mbg.generated Thu Jun 26 17:58:24 CST 2025 * @param user 用户信息
* @return 用户信息集合信息
*/ */
SysUser selectByPrimaryKey(Long userId); public List<SysUser> selectUnallocatedList(SysUser user);
/** /**
* This method was generated by MyBatis Generator. * 通过用户名查询用户
* This method corresponds to the database table sys_user
* *
* @mbg.generated Thu Jun 26 17:58:24 CST 2025 * @param userName 用户名
* @return 用户对象信息
*/ */
List<SysUser> selectAll(); public SysUser selectUserByUserName(String userName);
/** /**
* This method was generated by MyBatis Generator. * 通过用户ID查询用户
* This method corresponds to the database table sys_user
* *
* @mbg.generated Thu Jun 26 17:58:24 CST 2025 * @param userId 用户ID
* @return 用户对象信息
*/ */
int updateByPrimaryKey(SysUser record); public SysUser selectUserById(Long userId);
List<SysUser> selectUserList(SysUser user); /**
* 新增用户信息
*
* @param user 用户信息
* @return 结果
*/
public int insertUser(SysUser user);
/**
* 修改用户信息
*
* @param user 用户信息
* @return 结果
*/
public int updateUser(SysUser user);
/**
* 修改用户头像
*
* @param userName 用户名
* @param avatar 头像地址
* @return 结果
*/
public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar);
/**
* 重置用户密码
*
* @param userName 用户名
* @param password 密码
* @return 结果
*/
public int resetUserPwd(@Param("userName") String userName, @Param("password") String password);
/**
* 通过用户ID删除用户
*
* @param userId 用户ID
* @return 结果
*/
public int deleteUserById(Long userId);
/**
* 批量删除用户信息
*
* @param userIds 需要删除的用户ID
* @return 结果
*/
public int deleteUserByIds(Long[] userIds);
/**
* 校验用户名称是否唯一
*
* @param userName 用户名称
* @return 结果
*/
public SysUser checkUserNameUnique(String userName);
/**
* 校验手机号码是否唯一
*
* @param phonenumber 手机号码
* @return 结果
*/
public SysUser checkPhoneUnique(String phonenumber);
/**
* 校验email是否唯一
*
* @param email 用户邮箱
* @return 结果
*/
public SysUser checkEmailUnique(String email);
} }

View File

@ -1,14 +1,19 @@
package com.example.testspring.demos.modules.user.service; package com.example.testspring.demos.modules.user.service;
import com.example.testspring.demos.modules.user.entity.SysMenu;
import com.example.testspring.demos.modules.user.entity.SysUser; import com.example.testspring.demos.modules.user.entity.SysUser;
import java.util.List; import java.util.List;
public interface ISysUserService { /**
* 用户 业务层
*
* @author ruoyi
*/
public interface ISysUserService
{
public SysUser selectUserByUserName(String userName);
public List<SysUser> selectUserList(SysUser user); public List<SysUser> selectUserList(SysUser user);
// public List<SysMenu> selectMenuList();
} }

View File

@ -0,0 +1,37 @@
package com.example.testspring.demos.modules.user.service.impl;
import org.springframework.security.core.userdetails.User;
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 java.util.Map;
//@Service
//public class CustomUserDetailsService implements UserDetailsService {
//
// // 临时模拟数据库用户数据
// private final Map<String, UserDetails> users = Map.of(
// "admin", User.builder()
// .username("admin")
// .password("$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2") // 密码是 "admin123"
// .roles("ADMIN")
// .build(),
// "user", User.builder()
// .username("user")
// .password("$2a$10$jZ5uF5rKx1Yl2dCz3lqWDu5c8lD7vUwEf9XJ5oM3rH2gNk7Wm4i6t") // 密码是 "user123"
// .roles("USER")
// .build()
// );
//
// @Override
// public UserDetails loadUserByUsername(String username)
// throws UsernameNotFoundException {
//
// if (!users.containsKey(username)) {
// throw new UsernameNotFoundException("用户未找到: " + username);
// }
// return users.get(username);
// }
//}

View File

@ -0,0 +1,54 @@
package com.example.testspring.demos.modules.user.service.impl;
import com.example.testspring.demos.modules.common.utils.StringUtils;
import com.example.testspring.demos.modules.user.entity.LoginUser;
import com.example.testspring.demos.modules.user.entity.SysUser;
import com.example.testspring.demos.modules.user.service.ISysUserService;
//import org.hibernate.service.spi.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
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 java.util.HashSet;
import java.util.Set;
@Service
public class DatabaseUserDetailsService implements UserDetailsService {
private static final Logger log = LoggerFactory.getLogger(DatabaseUserDetailsService.class);
@Autowired
private ISysUserService sysUserService;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
SysUser user = sysUserService.selectUserByUserName(username);
if (StringUtils.isNull(user))
{
log.info("登录用户:{} 不存在.", username);
throw new UsernameNotFoundException("用户未找到: " + username);
}
return createLoginUser(user);
}
public UserDetails createLoginUser(SysUser user)
{
Set<String> permissions = new HashSet<>();
// permissions.addAll(["system:user:list", "system:user:query"]);
permissions.add("admin");
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissions);
}
}

View File

@ -1,29 +1,60 @@
package com.example.testspring.demos.modules.user.service.impl; package com.example.testspring.demos.modules.user.service.impl;
import com.example.testspring.demos.modules.user.entity.SysMenu;
import com.example.testspring.demos.modules.user.entity.SysUser; import com.example.testspring.demos.modules.user.entity.SysUser;
import com.example.testspring.demos.modules.user.mapper.SysMenuMapper;
import com.example.testspring.demos.modules.user.mapper.SysUserMapper; import com.example.testspring.demos.modules.user.mapper.SysUserMapper;
import com.example.testspring.demos.modules.user.service.ISysUserService; import com.example.testspring.demos.modules.user.service.ISysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/**
* 用户 业务层处理
*
* @author ruoyi
*/
@Service @Service
public class SysUserServiceImpl implements ISysUserService { public class SysUserServiceImpl implements ISysUserService
{
private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);
@Autowired @Autowired
private SysUserMapper sysUserMapper; private SysUserMapper userMapper;
@Autowired
private SysMenuMapper sysMenuMapper;
// @Autowired
// private SysRoleMapper roleMapper;
//
// @Autowired
// private SysPostMapper postMapper;
//
// @Autowired
// private SysUserRoleMapper userRoleMapper;
//
// @Autowired
// private SysUserPostMapper userPostMapper;
//
// @Autowired
// private ISysConfigService configService;
//
// @Autowired
// protected Validator validator;
@Override @Override
public List<SysUser> selectUserList(SysUser user) { public SysUser selectUserByUserName(String userName)
return sysUserMapper.selectUserList(user); {
return userMapper.selectUserByUserName(userName);
} }
@Override
public List<SysUser> selectUserList(SysUser user){
return userMapper.selectUserList(user);
}
} }

View File

@ -2,8 +2,8 @@ spring:
datasource: datasource:
type: com.alibaba.druid.pool.DruidDataSource # 如使用Druid则取消注释 type: com.alibaba.druid.pool.DruidDataSource # 如使用Druid则取消注释
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
# url: 'jdbc:mysql://localhost:3306/ruoyi-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8' url: 'jdbc:mysql://localhost:3306/ruoyi-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8'
url: 'jdbc:mysql://localhost:3306/kangda?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8' # url: 'jdbc:mysql://localhost:3306/kangda?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8'
username: root username: root
password: root password: root
# Druid 连接池专属配置区 (注意缩进对齐) # Druid 连接池专属配置区 (注意缩进对齐)

View File

@ -3,10 +3,6 @@ spring:
name: testspring name: testspring
# mvc:
# # ????, ?????swagger???????
# pathmatch:
# matching-strategy: ANT_PATH_MATCHER # ??ant????
profiles: profiles:
active: dev active: dev
@ -15,6 +11,14 @@ server:
port: 9992 port: 9992
app:
jwt:
# secret: "bXktc3VwZXItc2VjdXJlLWp3dC1zZWNyZXQtc2hvdWxkLWJlLXN1ZmZpY2llbnRseS1sb25nLTEyMzQ=" # Base64编码密钥
secret : "SGxb0sdMEEierPA99jMpG2F8Mqd+tqAQhpfZNo646CEI6pARnPSj4QzjVNzU2qAx+B9Q6nzwCgMI4tQkk/Pkfg=="
expiration-ms: 3600000 # 1小时单位毫秒
issuer: "my-application"
mybatis: mybatis:

View File

@ -1,100 +1,231 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.testspring.demos.modules.user.mapper.SysUserMapper"> <mapper namespace="com.example.testspring.demos.modules.user.mapper.SysUserMapper">
<resultMap id="BaseResultMap" type="com.example.testspring.demos.modules.user.entity.SysUser">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Thu Jun 26 17:58:24 CST 2025.
-->
<id column="user_id" jdbcType="BIGINT" property="userId" />
<result column="username" jdbcType="VARCHAR" property="username" />
<result column="password" jdbcType="VARCHAR" property="password" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="salt" jdbcType="VARCHAR" property="salt" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="mobile" jdbcType="VARCHAR" property="mobile" />
<result column="status" jdbcType="TINYINT" property="status" />
<result column="create_user_id" jdbcType="BIGINT" property="createUserId" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
</resultMap>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Thu Jun 26 17:58:24 CST 2025.
-->
delete from sys_user
where user_id = #{userId,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.example.testspring.demos.modules.user.entity.SysUser">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Thu Jun 26 17:58:24 CST 2025.
-->
insert into sys_user (user_id, username, password,
name, salt, email,
mobile, status, create_user_id,
create_time)
values (#{userId,jdbcType=BIGINT}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR},
#{name,jdbcType=VARCHAR}, #{salt,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR},
#{mobile,jdbcType=VARCHAR}, #{status,jdbcType=TINYINT}, #{createUserId,jdbcType=BIGINT},
#{createTime,jdbcType=TIMESTAMP})
</insert>
<update id="updateByPrimaryKey" parameterType="com.example.testspring.demos.modules.user.entity.SysUser">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Thu Jun 26 17:58:24 CST 2025.
-->
update sys_user
set username = #{username,jdbcType=VARCHAR},
password = #{password,jdbcType=VARCHAR},
name = #{name,jdbcType=VARCHAR},
salt = #{salt,jdbcType=VARCHAR},
email = #{email,jdbcType=VARCHAR},
mobile = #{mobile,jdbcType=VARCHAR},
status = #{status,jdbcType=TINYINT},
create_user_id = #{createUserId,jdbcType=BIGINT},
create_time = #{createTime,jdbcType=TIMESTAMP}
where user_id = #{userId,jdbcType=BIGINT}
</update>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Thu Jun 26 17:58:24 CST 2025.
-->
select user_id, username, password, name, salt, email, mobile, status, create_user_id,
create_time
from sys_user
where user_id = #{userId,jdbcType=BIGINT}
</select>
<select id="selectAll" resultMap="BaseResultMap">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Thu Jun 26 17:58:24 CST 2025.
-->
select user_id, username, password, name, salt, email, mobile, status, create_user_id,
create_time
from sys_user
</select>
<select id="selectUserList" resultType="com.example.testspring.demos.modules.user.entity.SysUser"> <resultMap type="com.example.testspring.demos.modules.user.entity.SysUser" id="SysUserResult">
select user_id, username, password, name, salt, email, mobile, status, create_user_id, <id property="userId" column="user_id" />
create_time <result property="deptId" column="dept_id" />
from sys_user <result property="userName" column="user_name" />
where 1=1 <result property="nickName" column="nick_name" />
<if test="username != null and username != ''"> <result property="email" column="email" />
and username like '%${username}%' <result property="phonenumber" column="phonenumber" />
</if> <result property="sex" column="sex" />
<if test="name != null and name != ''"> <result property="avatar" column="avatar" />
and name like '%${name}%' <result property="password" column="password" />
</if> <result property="status" column="status" />
<if test="status != null"> <result property="delFlag" column="del_flag" />
and status = #{status} <result property="loginIp" column="login_ip" />
</if> <result property="loginDate" column="login_date" />
</select> <result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<!-- <association property="dept" column="dept_id" javaType="SysDept" resultMap="deptResult" />-->
<!-- <collection property="roles" javaType="java.util.List" resultMap="RoleResult" />-->
</resultMap>
<!-- <resultMap id="deptResult" type="SysDept">-->
<!-- <id property="deptId" column="dept_id" />-->
<!-- <result property="parentId" column="parent_id" />-->
<!-- <result property="deptName" column="dept_name" />-->
<!-- <result property="ancestors" column="ancestors" />-->
<!-- <result property="orderNum" column="order_num" />-->
<!-- <result property="leader" column="leader" />-->
<!-- <result property="status" column="dept_status" />-->
<!-- </resultMap>-->
<!-- <resultMap id="RoleResult" type="SysRole">-->
<!-- <id property="roleId" column="role_id" />-->
<!-- <result property="roleName" column="role_name" />-->
<!-- <result property="roleKey" column="role_key" />-->
<!-- <result property="roleSort" column="role_sort" />-->
<!-- <result property="dataScope" column="data_scope" />-->
<!-- <result property="status" column="role_status" />-->
<!-- </resultMap>-->
<sql id="selectUserVo">
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
left join sys_user_role ur on u.user_id = ur.user_id
left join sys_role r on r.role_id = ur.role_id
</sql>
<sql id="selectUserVo1">
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
from sys_user u
</sql>
<select id="selectUserList" parameterType="com.example.testspring.demos.modules.user.entity.SysUser" resultMap="SysUserResult">
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
where u.del_flag = '0'
<if test="userId != null and userId != 0">
AND u.user_id = #{userId}
</if>
<if test="userName != null and userName != ''">
AND u.user_name like concat('%', #{userName}, '%')
</if>
<if test="status != null and status != ''">
AND u.status = #{status}
</if>
<if test="phonenumber != null and phonenumber != ''">
AND u.phonenumber like concat('%', #{phonenumber}, '%')
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
AND date_format(u.create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
AND date_format(u.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
</if>
<if test="deptId != null and deptId != 0">
AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
</if>
<!-- 数据范围过滤 -->
${params.dataScope}
</select>
<select id="selectAllocatedList" parameterType="com.example.testspring.demos.modules.user.entity.SysUser" resultMap="SysUserResult">
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
left join sys_user_role ur on u.user_id = ur.user_id
left join sys_role r on r.role_id = ur.role_id
where u.del_flag = '0' and r.role_id = #{roleId}
<if test="userName != null and userName != ''">
AND u.user_name like concat('%', #{userName}, '%')
</if>
<if test="phonenumber != null and phonenumber != ''">
AND u.phonenumber like concat('%', #{phonenumber}, '%')
</if>
<!-- 数据范围过滤 -->
${params.dataScope}
</select>
<select id="selectUnallocatedList" parameterType="com.example.testspring.demos.modules.user.entity.SysUser" resultMap="SysUserResult">
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
left join sys_user_role ur on u.user_id = ur.user_id
left join sys_role r on r.role_id = ur.role_id
where u.del_flag = '0' and (r.role_id != #{roleId} or r.role_id IS NULL)
and u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and ur.role_id = #{roleId})
<if test="userName != null and userName != ''">
AND u.user_name like concat('%', #{userName}, '%')
</if>
<if test="phonenumber != null and phonenumber != ''">
AND u.phonenumber like concat('%', #{phonenumber}, '%')
</if>
<!-- 数据范围过滤 -->
${params.dataScope}
</select>
<select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
<!-- <include refid="selectUserVo1"/>-->
<!-- where u.user_name = #{userName} and u.del_flag = '0'-->
select user_id, user_name, nick_name, email, password, status
from sys_user
where user_name = #{userName} and del_flag = '0'
</select>
<select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
<include refid="selectUserVo"/>
where u.user_id = #{userId}
</select>
<select id="checkUserNameUnique" parameterType="String" resultMap="SysUserResult">
select user_id, user_name from sys_user where user_name = #{userName} and del_flag = '0' limit 1
</select>
<select id="checkPhoneUnique" parameterType="String" resultMap="SysUserResult">
select user_id, phonenumber from sys_user where phonenumber = #{phonenumber} and del_flag = '0' limit 1
</select>
<select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult">
select user_id, email from sys_user where email = #{email} and del_flag = '0' limit 1
</select>
<insert id="insertUser" parameterType="com.example.testspring.demos.modules.user.entity.SysUser" useGeneratedKeys="true" keyProperty="userId">
insert into sys_user(
<if test="userId != null and userId != 0">user_id,</if>
<if test="deptId != null and deptId != 0">dept_id,</if>
<if test="userName != null and userName != ''">user_name,</if>
<if test="nickName != null and nickName != ''">nick_name,</if>
<if test="email != null and email != ''">email,</if>
<if test="avatar != null and avatar != ''">avatar,</if>
<if test="phonenumber != null and phonenumber != ''">phonenumber,</if>
<if test="sex != null and sex != ''">sex,</if>
<if test="password != null and password != ''">password,</if>
<if test="status != null and status != ''">status,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="remark != null and remark != ''">remark,</if>
create_time
)values(
<if test="userId != null and userId != ''">#{userId},</if>
<if test="deptId != null and deptId != ''">#{deptId},</if>
<if test="userName != null and userName != ''">#{userName},</if>
<if test="nickName != null and nickName != ''">#{nickName},</if>
<if test="email != null and email != ''">#{email},</if>
<if test="avatar != null and avatar != ''">#{avatar},</if>
<if test="phonenumber != null and phonenumber != ''">#{phonenumber},</if>
<if test="sex != null and sex != ''">#{sex},</if>
<if test="password != null and password != ''">#{password},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="remark != null and remark != ''">#{remark},</if>
sysdate()
)
</insert>
<update id="updateUser" parameterType="com.example.testspring.demos.modules.user.entity.SysUser">
update sys_user
<set>
<if test="deptId != null and deptId != 0">dept_id = #{deptId},</if>
<if test="userName != null and userName != ''">user_name = #{userName},</if>
<if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>
<if test="email != null ">email = #{email},</if>
<if test="phonenumber != null ">phonenumber = #{phonenumber},</if>
<if test="sex != null and sex != ''">sex = #{sex},</if>
<if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
<if test="password != null and password != ''">password = #{password},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if>
<if test="loginDate != null">login_date = #{loginDate},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="remark != null">remark = #{remark},</if>
update_time = sysdate()
</set>
where user_id = #{userId}
</update>
<update id="updateUserStatus" parameterType="com.example.testspring.demos.modules.user.entity.SysUser">
update sys_user set status = #{status} where user_id = #{userId}
</update>
<update id="updateUserAvatar" parameterType="com.example.testspring.demos.modules.user.entity.SysUser">
update sys_user set avatar = #{avatar} where user_name = #{userName}
</update>
<update id="resetUserPwd" parameterType="com.example.testspring.demos.modules.user.entity.SysUser">
update sys_user set password = #{password} where user_name = #{userName}
</update>
<delete id="deleteUserById" parameterType="Long">
update sys_user set del_flag = '2' where user_id = #{userId}
</delete>
<delete id="deleteUserByIds" parameterType="Long">
update sys_user set del_flag = '2' where user_id in
<foreach collection="array" item="userId" open="(" separator="," close=")">
#{userId}
</foreach>
</delete>
</mapper> </mapper>