diff --git a/gym-manage-api/gym-member/pom.xml b/gym-manage-api/gym-member/pom.xml index c74a4c3..2f57396 100644 --- a/gym-manage-api/gym-member/pom.xml +++ b/gym-manage-api/gym-member/pom.xml @@ -27,6 +27,11 @@ manage-db ${project.version} + + cn.novalon.gym.manage + manage-sys + ${project.version} + org.springframework.boot spring-boot-starter-webflux @@ -108,26 +113,12 @@ com.github.binarywang weixin-java-miniapp 4.6.0 - + + com.github.binarywang weixin-java-mp 4.6.0 - - - io.jsonwebtoken - jjwt-api - - - io.jsonwebtoken - jjwt-impl - runtime - - - io.jsonwebtoken - jjwt-jackson - runtime - cn.hutool hutool-all diff --git a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/handler/MemberHandler.java b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/handler/MemberHandler.java index fbd6e99..4d66c7a 100644 --- a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/handler/MemberHandler.java +++ b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/handler/MemberHandler.java @@ -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 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 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 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 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 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 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 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 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 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); diff --git a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatAuthServiceImpl.java b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatAuthServiceImpl.java index 1a858d7..5d7904f 100644 --- a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatAuthServiceImpl.java +++ b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatAuthServiceImpl.java @@ -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 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 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 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(); - } } diff --git a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/util/AuthUtil.java b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/util/AuthUtil.java new file mode 100644 index 0000000..c2a9ea6 --- /dev/null +++ b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/util/AuthUtil.java @@ -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); + } +} \ No newline at end of file diff --git a/gym-manage-api/manage-app/pom.xml b/gym-manage-api/manage-app/pom.xml index ced508a..5eecb9d 100644 --- a/gym-manage-api/manage-app/pom.xml +++ b/gym-manage-api/manage-app/pom.xml @@ -43,6 +43,11 @@ gym-member ${project.version} + + cn.novalon.gym.manage + gym-checkIn + ${project.version} + org.springframework.boot spring-boot-starter-webflux diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/config/SecurityConfig.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/config/SecurityConfig.java index 03ca331..4df2a4b 100644 --- a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/config/SecurityConfig.java +++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/config/SecurityConfig.java @@ -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() diff --git a/gym-manage-api/pom.xml b/gym-manage-api/pom.xml index 9681760..70df0c1 100644 --- a/gym-manage-api/pom.xml +++ b/gym-manage-api/pom.xml @@ -43,7 +43,8 @@ manage-notify manage-file gym-member - + gym-checkIn +