更新会员模块和系统安全配置
This commit is contained in:
@@ -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>
|
||||
|
||||
+14
-50
@@ -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);
|
||||
|
||||
+12
-54
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+32
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
+1
-2
@@ -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()
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
<module>manage-notify</module>
|
||||
<module>manage-file</module>
|
||||
<module>gym-member</module>
|
||||
</modules>
|
||||
<module>gym-checkIn</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
Reference in New Issue
Block a user