feat: 引入spring security和oauth2库还有jwt库,实现基本的注册功能和登录功能

This commit is contained in:
2025-11-19 23:12:35 +08:00
parent 09a5674672
commit 9b28ef0a37
23 changed files with 496 additions and 34 deletions

1
.idea/misc.xml generated
View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="KubernetesApiProvider"><![CDATA[{}]]></component>
<component name="MavenProjectsManager"> <component name="MavenProjectsManager">
<option name="originalFiles"> <option name="originalFiles">
<list> <list>

View File

@@ -28,5 +28,35 @@
<artifactId>aioj-backend-common</artifactId> <artifactId>aioj-backend-common</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- OAuth2 Client -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.13.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.13.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.13.0</version>
<scope>runtime</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,5 +0,0 @@
package cn.meowrain.aioj.backend.userservice.common.constants;
public class UserServiceChainConstants {
protected static final String register_req_verify = "register_req_verify";
}

View File

@@ -4,11 +4,15 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor @RequiredArgsConstructor
public enum ChianMarkEnums { public enum ChainMarkEnums {
/** /**
* 用户注册请求验证 * 用户注册请求验证
*/ */
USER_REGISTER_REQ_PARAM_VERIFY("USER_REGISTER_REQ_PARAM_VERIFY"); USER_REGISTER_REQ_PARAM_VERIFY("USER_REGISTER_REQ_PARAM_VERIFY"),
/**
* 用户登录请求验证
*/
USER_LOGIN_REQ_PARAM_VERIFY("USER_LOGIN_REQ_PARAM_VERIFY");
@Getter @Getter
private final String markName; private final String markName;

View File

@@ -1,5 +1,6 @@
package cn.meowrain.aioj.backend.userservice.config; package cn.meowrain.aioj.backend.userservice.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans; import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -9,4 +10,6 @@ import org.springframework.context.annotation.Configuration;
@ComponentScan("cn.meowrain.aioj.backend.framework.banner") @ComponentScan("cn.meowrain.aioj.backend.framework.banner")
}) })
public class FrameworkConfiguration { public class FrameworkConfiguration {
} }

View File

@@ -0,0 +1,21 @@
package cn.meowrain.aioj.backend.userservice.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@Data
@ConfigurationProperties(value = JwtPropertiesConfiguration.PREFIX)
public class JwtPropertiesConfiguration {
public static final String PREFIX = "jwt";
/**
* JWT 密钥(必须 32 字节以上)
*/
private String secret;
/**
* 过期时间(单位:毫秒)
*/
private Long expire;
}

View File

@@ -0,0 +1,56 @@
package cn.meowrain.aioj.backend.userservice.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import cn.meowrain.aioj.backend.userservice.security.JwtAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfiguration(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/auth/**",
"/doc.html",
"/swagger-ui/**",
"/swagger-resources/**",
"/webjars/**",
"/v3/api-docs/**",
"/favicon.ico"
)
.permitAll()
.anyRequest().authenticated())
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
}

View File

@@ -1,7 +1,36 @@
package cn.meowrain.aioj.backend.userservice.controller; package cn.meowrain.aioj.backend.userservice.controller;
import cn.meowrain.aioj.backend.framework.web.Result;
import cn.meowrain.aioj.backend.framework.web.Results;
import cn.meowrain.aioj.backend.userservice.dto.req.UserLoginRequest;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
import cn.meowrain.aioj.backend.userservice.dto.resp.UserLoginResponse;
import cn.meowrain.aioj.backend.userservice.service.UserService;
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; import org.springframework.web.bind.annotation.RestController;
@RestController("/user") @RequiredArgsConstructor
@RestController()
@RequestMapping("/auth")
public class UserController { public class UserController {
private final UserService userService;
@PostMapping("/register")
public Result<Long> register(@RequestBody UserRegisterRequest userRegisterRequest) {
Long l = userService.userRegister(userRegisterRequest);
return Results.success(l);
}
@PostMapping("/login")
public Result<UserLoginResponse> login(@RequestBody UserLoginRequest userLoginRequest) {
UserLoginResponse userLoginResponse = userService.userLogin(userLoginRequest);
return Results.success(userLoginResponse);
}
} }

View File

@@ -0,0 +1,37 @@
package cn.meowrain.aioj.backend.userservice.dto.chains;
import cn.meowrain.aioj.backend.framework.designpattern.chains.AbstractChianHandler;
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.exception.ClientException;
import cn.meowrain.aioj.backend.userservice.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.userservice.dto.req.UserLoginRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class UserLoginRequestParamVerifyChain implements AbstractChianHandler<UserLoginRequest> {
@Override
public void handle(UserLoginRequest requestParam) {
if (StringUtils.isAnyBlank(requestParam.getUserAccount(), requestParam.getUserPassword())) {
throw new ClientException("参数为空", ErrorCode.PARAMS_ERROR);
}
if (requestParam.getUserAccount().length() < 4) {
throw new ClientException("账号长度不小于4位", ErrorCode.PARAMS_ERROR);
}
if (requestParam.getUserPassword().length() < 8) {
throw new ClientException("密码长度不小于8位", ErrorCode.PARAMS_ERROR);
}
}
@Override
public String mark() {
return ChainMarkEnums.USER_LOGIN_REQ_PARAM_VERIFY.getMarkName();
}
@Override
public int getOrder() {
return 10;
}
}

View File

@@ -3,7 +3,7 @@ package cn.meowrain.aioj.backend.userservice.dto.chains;
import cn.meowrain.aioj.backend.framework.designpattern.chains.AbstractChianHandler; import cn.meowrain.aioj.backend.framework.designpattern.chains.AbstractChianHandler;
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode; import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.exception.ClientException; import cn.meowrain.aioj.backend.framework.exception.ClientException;
import cn.meowrain.aioj.backend.userservice.common.enums.ChianMarkEnums; import cn.meowrain.aioj.backend.userservice.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest; import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -33,7 +33,7 @@ public class UserRegisterRequestParamVerifyChain implements AbstractChianHandler
@Override @Override
public String mark() { public String mark() {
return ChianMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName(); return ChainMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName();
} }
@Override @Override

View File

@@ -0,0 +1,9 @@
package cn.meowrain.aioj.backend.userservice.dto.chains.context;
import cn.meowrain.aioj.backend.framework.designpattern.chains.CommonChainContext;
import cn.meowrain.aioj.backend.userservice.dto.req.UserLoginRequest;
import org.springframework.stereotype.Component;
@Component
public class UserLoginRequestParamVerifyContext extends CommonChainContext<UserLoginRequest> {
}

View File

@@ -1,13 +1,10 @@
package cn.meowrain.aioj.backend.userservice.dto.req; package cn.meowrain.aioj.backend.userservice.dto.req;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.Builder;
import lombok.Data; import lombok.Data;
@Data @Data
@Builder
public class UserLoginRequest { public class UserLoginRequest {
private String userAccount; private String userAccount;
private String userPassword; private String userPassword;
HttpServletRequest request;
} }

View File

@@ -1,10 +1,9 @@
package cn.meowrain.aioj.backend.userservice.dto.req; package cn.meowrain.aioj.backend.userservice.dto.req;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
@Data @Data
@Builder
public class UserRegisterRequest { public class UserRegisterRequest {
private String userAccount; private String userAccount;
private String userPassword; private String userPassword;

View File

@@ -1,5 +1,6 @@
package cn.meowrain.aioj.backend.userservice.dto.resp; package cn.meowrain.aioj.backend.userservice.dto.resp;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
@@ -7,11 +8,26 @@ import java.util.Date;
@Data @Data
public class UserLoginResponse implements Serializable { public class UserLoginResponse implements Serializable {
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/** /**
* 用户 id * 用户账号
*/ */
private Long id; private String userAccount;
/**
* 开放平台id
*/
private String unionId;
/**
* 公众号openId
*/
private String mpOpenId;
/** /**
* 用户昵称 * 用户昵称
@@ -43,5 +59,17 @@ public class UserLoginResponse implements Serializable {
*/ */
private Date updateTime; private Date updateTime;
/**
* 是否删除
*/
private Integer isDelete;
/**
* JWT令牌登录成功返回
*/
private String token;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
} }

View File

@@ -0,0 +1,35 @@
package cn.meowrain.aioj.backend.userservice.security;
import cn.meowrain.aioj.backend.userservice.dao.entity.User;
import cn.meowrain.aioj.backend.userservice.dao.mapper.UserMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.security.core.GrantedAuthority;
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 java.util.Collection;
import java.util.Collections;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserMapper userMapper;
public CustomUserDetailsService(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUserAccount, username));
if (user == null) {
throw new UsernameNotFoundException("用户不存在: " + username);
}
Collection<? extends GrantedAuthority> authorities = Collections
.singletonList(new SimpleGrantedAuthority(user.getUserRole()));
return new org.springframework.security.core.userdetails.User(user.getUserAccount(), user.getUserPassword(),
authorities);
}
}

View File

@@ -0,0 +1,76 @@
package cn.meowrain.aioj.backend.userservice.security;
import cn.meowrain.aioj.backend.userservice.dao.entity.User;
import cn.meowrain.aioj.backend.userservice.dao.mapper.UserMapper;
import cn.meowrain.aioj.backend.userservice.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
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;
import java.util.Collections;
import java.util.List;
/**
* JWT请求过滤器解析 Authorization 头中的 Bearer token 并设置认证上下文
*/
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final UserMapper userMapper;
public JwtAuthenticationFilter(JwtUtil jwtUtil, UserMapper userMapper) {
this.jwtUtil = jwtUtil;
this.userMapper = userMapper;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String header = request.getHeader("Authorization");
if (!StringUtils.hasText(header) || !header.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
String token = header.substring(7);
if (!jwtUtil.isTokenValid(token)) {
filterChain.doFilter(request, response);
return;
}
try {
Claims claims = jwtUtil.parseClaims(token);
String account = claims.getSubject();
Long userId = claims.get("userId", Long.class);
// 避免重复设置
if (StringUtils.hasText(account) && SecurityContextHolder.getContext().getAuthentication() == null) {
// 可选:从数据库再查一次,保证用户未被删除/封禁
User user = userMapper.selectById(userId);
if (user == null || !StringUtils.hasText(user.getUserRole())) {
filterChain.doFilter(request, response);
return;
}
List<SimpleGrantedAuthority> authorities = Collections
.singletonList(new SimpleGrantedAuthority(user.getUserRole()));
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(account,
null, authorities);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ignored) {
// 解析失败直接继续,不抛出,保持无状态
}
filterChain.doFilter(request, response);
}
}

View File

@@ -1,16 +1,23 @@
package cn.meowrain.aioj.backend.userservice.service; package cn.meowrain.aioj.backend.userservice.service;
import cn.meowrain.aioj.backend.userservice.dao.entity.User; import cn.meowrain.aioj.backend.userservice.dao.entity.User;
import cn.meowrain.aioj.backend.userservice.dto.req.UserLoginRequest;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest; import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
import cn.meowrain.aioj.backend.userservice.dto.resp.UserLoginResponse;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
public interface UserService extends IService<User> { public interface UserService extends IService<User> {
/** /**
* 用户注册 * 用户注册
* @param request {@link UserRegisterRequest} * @param request {@link UserRegisterRequest}
* @return * @return {@link Long}
*/ */
Long userRegister(UserRegisterRequest request); Long userRegister(UserRegisterRequest request);
/**
* 用户登录
* @param request {@link UserLoginRequest}
* @return {@link UserLoginResponse}
*/
UserLoginResponse userLogin(UserLoginRequest request);
} }

View File

@@ -1,33 +1,48 @@
package cn.meowrain.aioj.backend.userservice.service.impl; package cn.meowrain.aioj.backend.userservice.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode; import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.exception.ServiceException; import cn.meowrain.aioj.backend.framework.exception.ServiceException;
import cn.meowrain.aioj.backend.userservice.common.enums.ChianMarkEnums; import cn.meowrain.aioj.backend.userservice.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.userservice.dao.entity.User; import cn.meowrain.aioj.backend.userservice.dao.entity.User;
import cn.meowrain.aioj.backend.userservice.dao.mapper.UserMapper; import cn.meowrain.aioj.backend.userservice.dao.mapper.UserMapper;
import cn.meowrain.aioj.backend.userservice.dto.chains.context.UserLoginRequestParamVerifyContext;
import cn.meowrain.aioj.backend.userservice.dto.chains.context.UserRegisterRequestParamVerifyContext; import cn.meowrain.aioj.backend.userservice.dto.chains.context.UserRegisterRequestParamVerifyContext;
import cn.meowrain.aioj.backend.userservice.dto.req.UserLoginRequest;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest; import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
import cn.meowrain.aioj.backend.userservice.dto.resp.UserLoginResponse;
import cn.meowrain.aioj.backend.userservice.service.UserService; import cn.meowrain.aioj.backend.userservice.service.UserService;
import cn.meowrain.aioj.backend.userservice.utils.JwtUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.util.Date;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
private final UserRegisterRequestParamVerifyContext userRegisterRequestParamVerifyContext; private final UserRegisterRequestParamVerifyContext userRegisterRequestParamVerifyContext;
private final String SALT = "meowrain"; private final UserLoginRequestParamVerifyContext userLoginRequestParamVerifyContext;
private final PasswordEncoder passwordEncoder;
private final JwtUtil jwtUtil;
@Override @Override
public Long userRegister(UserRegisterRequest request) { public Long userRegister(UserRegisterRequest request) {
userRegisterRequestParamVerifyContext.handler(ChianMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName(), request); userRegisterRequestParamVerifyContext.handler(ChainMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName(),
// 加密 request);
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + request.getUserPassword()).getBytes()); // 使用 BCrypt 加密密码
User user = new User().setUserAccount(request.getUserAccount()).setUserPassword(encryptPassword); Date now = new Date();
String encryptPassword = passwordEncoder.encode(request.getUserPassword());
User user = new User().setUserAccount(request.getUserAccount()).setUserPassword(encryptPassword)
.setUserRole("user").setCreateTime(now).setUpdateTime(now);
try { try {
// 需要修改表,使得用户名是唯一的
this.save(user); this.save(user);
} catch (DuplicateKeyException e) { } catch (DuplicateKeyException e) {
log.error("重复创建用户"); log.error("重复创建用户");
@@ -36,4 +51,29 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return user.getId(); return user.getId();
} }
@Override
public UserLoginResponse userLogin(UserLoginRequest requestParam) {
// 1.校验
userLoginRequestParamVerifyContext.handler(ChainMarkEnums.USER_LOGIN_REQ_PARAM_VERIFY.getMarkName(),
requestParam);
User user = this.baseMapper.selectOne(Wrappers.lambdaQuery(User.class)
.eq(User::getUserAccount, requestParam.getUserAccount()));
if (ObjectUtil.isNull(user)
|| !passwordEncoder.matches(requestParam.getUserPassword(), user.getUserPassword())) {
throw new ServiceException("用户不存在或者密码错误", ErrorCode.NOT_LOGIN_ERROR);
}
// 生成 JWT
String token = jwtUtil.generateToken(user);
UserLoginResponse resp = new UserLoginResponse();
resp.setId(user.getId());
resp.setUserAccount(user.getUserAccount());
resp.setUserAvatar(user.getUserAvatar());
resp.setUserProfile(user.getUserProfile());
resp.setUserRole(user.getUserRole());
resp.setCreateTime(user.getCreateTime());
resp.setUpdateTime(user.getUpdateTime());
resp.setToken(token);
return resp;
}
} }

View File

@@ -0,0 +1,59 @@
package cn.meowrain.aioj.backend.userservice.utils;
import cn.meowrain.aioj.backend.userservice.config.JwtPropertiesConfiguration;
import cn.meowrain.aioj.backend.userservice.dao.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* JWT工具类
*/
@RequiredArgsConstructor
@Component
public class JwtUtil {
private final JwtPropertiesConfiguration jwtConfig;
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(jwtConfig.getSecret().getBytes());
}
public String generateToken(User user) {
long now = System.currentTimeMillis();
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
claims.put("role", user.getUserRole());
return Jwts.builder()
.subject(user.getUserAccount())
.issuedAt(new Date(now))
.expiration(new Date(now + jwtConfig.getExpire()))
.claims(claims)
.signWith(getSigningKey(), Jwts.SIG.HS256)
.compact();
}
public Claims parseClaims(String token) {
return Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
public boolean isTokenValid(String token) {
try {
Claims claims = parseClaims(token);
Date expiration = claims.getExpiration();
return expiration != null && expiration.after(new Date());
} catch (Exception e) {
return false;
}
}
}

View File

@@ -8,7 +8,7 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.0.0.10/aioj_dev url: jdbc:mysql://10.0.0.10/aioj_dev
username: root username: root
password: 123456 password: root
cloud: cloud:
nacos: nacos:
discovery: discovery:

View File

@@ -8,4 +8,4 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.0.0.10/aioj_prod url: jdbc:mysql://10.0.0.10/aioj_prod
username: root username: root
password: 123456 password: root

View File

@@ -17,7 +17,7 @@ springdoc:
group-configs: group-configs:
- group: 'default' - group: 'default'
paths-to-match: '/**' paths-to-match: '/**'
packages-to-scan: cn.meowrain.aioj packages-to-scan: cn.meowrain.aioj.backend.userservice.controller
knife4j: knife4j:
basic: basic:
enable: true enable: true
@@ -27,3 +27,6 @@ mybatis-plus:
configuration: configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:/mapper/**/*.xml mapper-locations: classpath*:/mapper/**/*.xml
jwt:
secret: "12345678901234567890123456789012" # 至少32字节
expire: 86400000 # 24小时单位毫秒

43
pom.xml
View File

@@ -44,10 +44,17 @@
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency> </dependency>
<!-- <dependency>--> <!-- 引入cache-->
<!-- <groupId>org.mybatis.spring.boot</groupId>--> <dependency>
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>--> <groupId>org.springframework.boot</groupId>
<!-- </dependency>--> <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- </dependency>-->
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
@@ -128,6 +135,34 @@
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version> <version>4.5.0</version>
</dependency> </dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.5.7</version>
</dependency>
<!-- OAuth2 Client -->
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-oauth2-client -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-oauth2-client -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
<version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-test -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>6.5.7</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-oauth2-resource-server -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<build> <build>