更新会员模块和系统安全配置

This commit is contained in:
future
2026-05-27 03:21:38 +08:00
parent e673d96f6f
commit ddb77db605
7 changed files with 73 additions and 123 deletions
+7 -16
View File
@@ -27,6 +27,11 @@
<artifactId>manage-db</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cn.novalon.gym.manage</groupId>
<artifactId>manage-sys</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
@@ -108,26 +113,12 @@
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>4.6.0</version>
</dependency> <dependency>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.6.0</version>
</dependency>
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
@@ -8,12 +8,11 @@ import cn.novalon.gym.manage.member.service.MemberService;
import cn.novalon.gym.manage.member.service.WechatAuthService;
import cn.novalon.gym.manage.member.service.WechatOfficialService;
import cn.novalon.gym.manage.member.util.AesUtil;
import cn.novalon.gym.manage.member.util.AuthUtil;
import cn.novalon.gym.manage.sys.security.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
@@ -37,6 +36,7 @@ public class MemberHandler {
private final WechatOfficialService wechatOfficialService;
private final JwtTokenProvider jwtTokenProvider;
private final WechatProperties wechatProperties;
private final AuthUtil authUtil;
/**
* 获取会员信息
@@ -46,10 +46,7 @@ public class MemberHandler {
*/
public Mono<ServerResponse> getMemberInfo(ServerRequest request) {
String memberIdStr = request.headers().firstHeader("X-Member-Id");
long memberId = NumberUtils.toLong(memberIdStr,0L);
if (memberId <= 0) throw new IllegalArgumentException("获取会员信息失败: memberId 无效");
Long memberId = authUtil.getMemberIdOrThrow(request);
log.info("获取会员信息, memberId: {}", memberId);
@@ -76,10 +73,7 @@ public class MemberHandler {
*/
public Mono<ServerResponse> updateMemberInfo(ServerRequest request) {
String memberIdStr = request.headers().firstHeader("X-Member-Id");
long memberId = NumberUtils.toLong(memberIdStr, 0L);
if (memberId <= 0) throw new IllegalArgumentException("更新会员信息失败: memberId 无效");
Long memberId = authUtil.getMemberIdOrThrow(request);
log.info("更新会员信息, memberId: {}", memberId);
@@ -98,14 +92,11 @@ public class MemberHandler {
*/
public Mono<ServerResponse> bindPhone(ServerRequest request) {
String memberIdStr = request.headers().firstHeader("X-Member-Id");
Long memberId = NumberUtils.toLong(memberIdStr, 0L);
if (memberId <= 0) throw new IllegalArgumentException("绑定手机号失败: memberId 无效");
Long memberId = authUtil.getMemberIdOrThrow(request);
String phoneCode = request.queryParam("phoneCode").orElse("");
if (phoneCode == null || phoneCode.trim().isEmpty()) throw new IllegalArgumentException("手机号code不能为空");
if (phoneCode.trim().isEmpty()) throw new IllegalArgumentException("手机号code不能为空");
log.info("收到绑定手机号请求, memberId: {}, phoneCode: {}", memberId, phoneCode);
@@ -124,10 +115,7 @@ public class MemberHandler {
*/
public Mono<ServerResponse> checkSubscribeStatus(ServerRequest request) {
String memberIdStr = request.headers().firstHeader("X-Member-Id");
long memberId = NumberUtils.toLong(memberIdStr,0L);
if (memberId <= 0) throw new IllegalArgumentException("查询服务号关注状态失败: memberId 无效");
Long memberId = authUtil.getMemberIdOrThrow(request);
log.info("查询服务号关注状态, memberId: {}", memberId);
@@ -148,6 +136,8 @@ public class MemberHandler {
*/
public Mono<ServerResponse> adminUpdatePhone(ServerRequest request) {
Long adminId = authUtil.getMemberIdOrThrow(request);
String memberIdStr = request.pathVariable("id");
long memberId = NumberUtils.toLong(memberIdStr, 0L);
@@ -184,18 +174,12 @@ public class MemberHandler {
*/
public Mono<ServerResponse> adminGetMemberInfo(ServerRequest request) {
String authorization = request.headers().firstHeader(HttpHeaders.AUTHORIZATION);
if (authorization == null || !authorization.startsWith("Bearer ")) throw new IllegalArgumentException("无权访问");
authorization = authorization.substring(7);
// 验证token并获取memberId
if (!jwtTokenProvider.validateToken(authorization)) throw new IllegalArgumentException("Authorization 无效");
Long adminId = authUtil.getMemberIdOrThrow(request);
String memberIdStr = request.pathVariable("id");
long memberId = NumberUtils.toLong(memberIdStr, 0L);
if(memberId <= 0) throw new IllegalArgumentException("会员ID格式错误");
Long adminId = jwtTokenProvider.getUserIdFromToken(authorization);
// TODO 多表查询:会员信息、团课信息、会员卡信息
return ServerResponse.ok()
@@ -212,17 +196,11 @@ public class MemberHandler {
*/
public Mono<ServerResponse> adminUpdateMemberInfo(ServerRequest request) {
String authorization = request.headers().firstHeader(HttpHeaders.AUTHORIZATION);
if (authorization == null || !authorization.startsWith("Bearer ")) throw new IllegalArgumentException("无权访问");
authorization = authorization.substring(7);
// 验证token并获取memberId
if (!jwtTokenProvider.validateToken(authorization)) throw new IllegalArgumentException("Authorization 无效");
Long adminId = authUtil.getMemberIdOrThrow(request);
String memberIdStr = request.pathVariable("id");
long memberId = NumberUtils.toLong(memberIdStr, 0L);
if(memberId <= 0) throw new IllegalArgumentException("会员ID格式错误");
Long adminId = jwtTokenProvider.getUserIdFromToken(authorization);
if(memberId <= 0L) throw new IllegalArgumentException("会员ID格式错误");
// TODO 多表查询:会员信息、团课信息、会员卡信息
@@ -239,14 +217,7 @@ public class MemberHandler {
*/
public Mono<ServerResponse> searchMembers(ServerRequest request) {
String authorization = request.headers().firstHeader(HttpHeaders.AUTHORIZATION);
if (authorization == null || !authorization.startsWith("Bearer ")) {
return ServerResponse.status(HttpStatus.UNAUTHORIZED).bodyValue("无权访问");
}
authorization = authorization.substring(7);
if (!jwtTokenProvider.validateToken(authorization)) {
return ServerResponse.status(HttpStatus.UNAUTHORIZED).bodyValue("Authorization 无效");
}
Long adminId = authUtil.getMemberIdOrThrow(request);
String keyword = request.queryParam("searchValue").orElse(null);
String filter = request.queryParam("filter").orElse(null);
@@ -282,14 +253,7 @@ public class MemberHandler {
*/
public Mono<ServerResponse> getAllMembers(ServerRequest request) {
String authorization = request.headers().firstHeader(HttpHeaders.AUTHORIZATION);
if (authorization == null || !authorization.startsWith("Bearer ")) {
return ServerResponse.status(HttpStatus.UNAUTHORIZED).bodyValue("无权访问");
}
authorization = authorization.substring(7);
if (!jwtTokenProvider.validateToken(authorization)) {
return ServerResponse.status(HttpStatus.UNAUTHORIZED).bodyValue("Authorization 无效");
}
Long adminId = authUtil.getMemberIdOrThrow(request);
int pageNum = NumberUtils.toInt(request.queryParam("pageNum").orElse("1"), 1);
int pageSize = NumberUtils.toInt(request.queryParam("pageSize").orElse("10"), 10);
@@ -1,6 +1,5 @@
package cn.novalon.gym.manage.member.service.impl;
import cn.novalon.gym.manage.common.config.JwtProperties;
import cn.novalon.gym.manage.member.config.WechatProperties;
import cn.novalon.gym.manage.member.dto.WechatLoginDto;
import cn.novalon.gym.manage.member.entity.Member;
@@ -14,20 +13,16 @@ import cn.novalon.gym.manage.member.util.EsSyncUtils;
import cn.novalon.gym.manage.member.util.MemberNoGenerator;
import cn.novalon.gym.manage.member.util.WechatPhoneUtil;
import cn.novalon.gym.manage.member.vo.WechatLoginVO;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import cn.novalon.gym.manage.sys.security.JwtTokenProvider;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;
/**
* 微信认证服务实现
@@ -43,11 +38,11 @@ public class WechatAuthServiceImpl implements WechatAuthService {
private final WechatApiService wechatApiService;
private final IMemberRepository memberRepository;
private final JwtProperties jwtProperties;
private final WechatProperties wechatProperties;
private final WechatPhoneUtil wechatPhoneUtil;
private final MemberESRepository memberESRepository;
private final EsSyncUtils esSyncUtils;
private final JwtTokenProvider jwtTokenProvider;
private EsSyncUtils.EntitySyncer<Member, MemberES, String> memberSyncer;
@@ -332,60 +327,23 @@ public class WechatAuthServiceImpl implements WechatAuthService {
member.getPhone() != null ? "已绑定" : "未绑定");
}
// 生成 Access Token(使用 Gateway 相同的方式)
String accessToken = generateJwtToken(member.getId(), "access");
// 生成 Refresh Token(有效期更长)
String refreshToken = generateJwtToken(member.getId(), "refresh", 7L * 24 * 60 * 60 * 1000);
// 统一使用 JwtTokenProvider 生成 Token
// 使用空的角色列表,会员不需要角色
List<String> roles = new ArrayList<>();
String accessToken = jwtTokenProvider.generateToken(String.valueOf(member.getId()), member.getId(), roles);
log.info("JWT Token 生成成功, memberId: {}", member.getId());
// 计算过期时间(秒)
long expiresIn = jwtProperties.getExpiration() / 1000;
// 使用 JwtTokenProvider 默认过期时间(86400 秒)
int expiresIn = 86400;
return WechatLoginVO.builder()
.memberId(member.getId())
.accessToken(accessToken)
.refreshToken(refreshToken)
.expiresIn((int) expiresIn)
.refreshToken(accessToken)
.expiresIn(expiresIn)
.isNewUser(isNewUser)
.needCompleteInfo(needCompleteInfo)
.build();
}
/**
* 生成 JWT Token(与 Gateway 一致)
*
* @param memberId 会员 ID
* @param tokenType Token 类型(access/refresh
* @return JWT Token 字符串
*/
private String generateJwtToken(Long memberId, String tokenType) {
return generateJwtToken(memberId, tokenType, jwtProperties.getExpiration());
}
/**
* 生成 JWT Token(自定义过期时间)
*
* @param memberId 会员 ID
* @param tokenType Token 类型(access/refresh
* @param expirationMs 过期时间(毫秒)
* @return JWT Token 字符串
*/
private String generateJwtToken(Long memberId, String tokenType, long expirationMs) {
SecretKey key = Keys.hmacShaKeyFor(jwtProperties.getSecret().getBytes(StandardCharsets.UTF_8));
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expirationMs);
Map<String, Object> claims = new HashMap<>();
claims.put("memberId", memberId);
claims.put("tokenType", tokenType);
return Jwts.builder()
.setClaims(claims)
.setSubject(String.valueOf(memberId))
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(key)
.compact();
}
}
@@ -0,0 +1,32 @@
package cn.novalon.gym.manage.member.util;
import cn.novalon.gym.manage.sys.security.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ResponseStatusException;
@Slf4j
@Component
@RequiredArgsConstructor
public class AuthUtil {
private final JwtTokenProvider jwtTokenProvider;
public String extractToken(ServerRequest request) {
String authorization = request.headers().firstHeader(HttpHeaders.AUTHORIZATION);
if (authorization == null || !authorization.startsWith("Bearer ")) return null;
return authorization.substring(7);
}
public Long getMemberIdOrThrow(ServerRequest request) {
String token = extractToken(request);
if (token == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "缺少 Token");
if (!jwtTokenProvider.validateToken(token)) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Token 无效或已过期");
if (jwtTokenProvider.getUserIdFromToken(token) <= 0L) throw new IllegalArgumentException("ID无效");
return jwtTokenProvider.getUserIdFromToken(token);
}
}
+5
View File
@@ -43,6 +43,11 @@
<artifactId>gym-member</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cn.novalon.gym.manage</groupId>
<artifactId>gym-checkIn</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
@@ -50,8 +50,7 @@ public class SecurityConfig {
spec.pathMatchers("/api/auth/**").permitAll()
.pathMatchers("/api/public/**").permitAll()
.pathMatchers("/ws/**").permitAll()
.pathMatchers("/actuator/**").permitAll()
.pathMatchers("/api/member/checkIn").permitAll();
.pathMatchers("/actuator/**").permitAll();
if (isDevOrTest) {
spec.pathMatchers("/swagger-ui.html").permitAll()
+2 -1
View File
@@ -43,7 +43,8 @@
<module>manage-notify</module>
<module>manage-file</module>
<module>gym-member</module>
</modules>
<module>gym-checkIn</module>
</modules>
<dependencyManagement>
<dependencies>