1. 完善小程序token验证
2. gateway服务添加openfein实现服务调用 3. 统一小程序uid和token生成方法
This commit is contained in:
parent
9354169741
commit
4fadcb5b8e
@ -63,6 +63,12 @@
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -75,6 +75,7 @@ public interface HDConstant {
|
||||
* 微信服务请求头中权限验证字段
|
||||
*/
|
||||
String WECHAT_SERVER_AUTHORIZATION_KEY = "WXUID";
|
||||
String WECHAT_SERVER_TOKEN_KEY = "WXTOKEN";
|
||||
|
||||
/**
|
||||
* 微信登录缓存数据前缀
|
||||
@ -82,6 +83,8 @@ public interface HDConstant {
|
||||
String openidPrefix = "hd:wechat:login:openid:";
|
||||
String unionidPrefix = "hd:wechat:login:unionid:";
|
||||
String sessionKeyPrefix = "hd:wechat:login:sessionKey:";
|
||||
String wxToken = "hd:wechat:login:token:";
|
||||
String tokenRandomStr = "hd:wechat:login:randomStr:";
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -76,9 +76,11 @@ public enum CodeMsg {
|
||||
REDIS_ERROR("D0410", "REDIS数据出错"),
|
||||
|
||||
// 小程序 w
|
||||
WECHAT_LOGIN_ERROR("W0400", "小程序登录出错"),
|
||||
WECHAT_SERRION_ERROR("W0401", "小程序登录态错误"),
|
||||
WECHAT_API_ERROR("W0402", "小程序接口调用异常");
|
||||
WECHAT_LOGIN_ERROR("W0400", "小程序登录异常"),
|
||||
WECHAT_SERRION_ERROR("W0401", "小程序登录态异常"),
|
||||
WECHAT_API_ERROR("W0402", "小程序接口调用异常"),
|
||||
WECHAT_NOT_LOGIN("W0403", "用户未登陆!"),
|
||||
WECHAT_TOKEN_INVALID("W0404", "无效token!");
|
||||
|
||||
String code;
|
||||
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
package com.evotech.hd.common.core.utils;
|
||||
|
||||
import cn.hutool.crypto.digest.MD5;
|
||||
|
||||
public class XCXUtil {
|
||||
|
||||
public static String wechatUid(String appid, String openid) {
|
||||
return MD5.create().digestHex(appid + openid + "HBYT");
|
||||
}
|
||||
|
||||
public static String wechatToken(String wuid, String randomStr) {
|
||||
return MD5.create().digestHex(wuid + randomStr + "HBYTXCX");
|
||||
}
|
||||
|
||||
}
|
||||
@ -108,6 +108,13 @@
|
||||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-starter-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- openfein -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -3,12 +3,13 @@ package com.evotech.hd.gateway;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@ComponentScan("com.evotech.hd.**")
|
||||
//@EnableFeignClients
|
||||
@EnableFeignClients
|
||||
public class GatewayServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package com.evotech.hd.gateway.config;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
|
||||
|
||||
|
||||
@Configuration
|
||||
public class FeignConfig {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Spring Cloud Gateway是基于WebFlux的,是ReactiveWeb,所以HttpMessageConverters不会自动注入。
|
||||
* @param converters
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
|
||||
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,6 +2,7 @@ package com.evotech.hd.gateway.oauth2;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@ -15,13 +16,17 @@ import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.web.server.authorization.AuthorizationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.evotech.hd.common.core.constant.HDConstant;
|
||||
import com.evotech.hd.common.core.enums.CodeMsg;
|
||||
import com.evotech.hd.common.core.utils.XCXUtil;
|
||||
import com.evotech.hd.common.redis.utils.RedisUtil;
|
||||
import com.evotech.hd.gateway.oauth2.service.WechatService;
|
||||
import com.evotech.hd.gateway.utils.TokenUtil;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@ -36,6 +41,8 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
|
||||
private RedisUtil redisUtil;
|
||||
@Resource
|
||||
private RSAKeyPair rsaKeyPair;
|
||||
@Resource
|
||||
private WechatService wechatService;
|
||||
|
||||
@Value("${yt.gateway.is_pass:false}")
|
||||
private boolean isPass;
|
||||
@ -55,12 +62,8 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
|
||||
}
|
||||
// 微信服务将UID放入请求头,验证一下
|
||||
if (uri.contains("/gateway/wechat/")) {
|
||||
String wuid = request.getHeaders().getFirst(HDConstant.WECHAT_SERVER_AUTHORIZATION_KEY);
|
||||
if (redisUtil.hasKey(HDConstant.sessionKeyPrefix + wuid) ) {
|
||||
return Mono.just(new AuthorizationDecision(true));
|
||||
} else {
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.TOKEN_INVALID.getCode(), CodeMsg.TOKEN_INVALID.getMsg(), uri));
|
||||
}
|
||||
return wechatTokenCheck(request, uri);
|
||||
|
||||
}
|
||||
// 2. token验证
|
||||
/**
|
||||
@ -81,6 +84,46 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
|
||||
.map(auth -> new AuthorizationDecision(checkAuthorities(token.substring(7), request, auth)))
|
||||
.defaultIfEmpty(new AuthorizationDecision(false));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 微信token权限验证逻辑
|
||||
* @param request
|
||||
* @param uri
|
||||
* @return
|
||||
*/
|
||||
private Mono<AuthorizationDecision> wechatTokenCheck(ServerHttpRequest request, String uri) {
|
||||
String wuid = request.getHeaders().getFirst(HDConstant.WECHAT_SERVER_AUTHORIZATION_KEY);
|
||||
String wxToken = request.getHeaders().getFirst(HDConstant.WECHAT_SERVER_TOKEN_KEY);
|
||||
if (!StringUtils.hasText(wxToken) || !StringUtils.hasText(wuid)) {
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.WECHAT_NOT_LOGIN.getCode(), CodeMsg.WECHAT_NOT_LOGIN.getMsg(), ""));
|
||||
}
|
||||
// 验证wuid
|
||||
if (!redisUtil.hasKey(HDConstant.openidPrefix + wuid)) {
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.WECHAT_LOGIN_ERROR.getCode(), CodeMsg.WECHAT_LOGIN_ERROR.getMsg(), uri));
|
||||
}
|
||||
// 验证token正确
|
||||
if (!redisUtil.hasKey(HDConstant.wxToken + wuid)) {
|
||||
// 缓存没了,过期了
|
||||
if (!wxToken.equals(XCXUtil.wechatToken(wuid, redisUtil.get(HDConstant.tokenRandomStr + wuid).toString()))) {
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.WECHAT_LOGIN_ERROR.getCode(), CodeMsg.WECHAT_LOGIN_ERROR.getMsg(), "token异常"));
|
||||
}
|
||||
Map<String, String> m = wechatService.tokenBuilder(wuid);
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.WECHAT_TOKEN_INVALID.getCode(), CodeMsg.WECHAT_TOKEN_INVALID.getMsg(), JSONUtil.toJsonStr(m)));
|
||||
} else {
|
||||
// 缓存还有
|
||||
if (wxToken.equals(redisUtil.get(HDConstant.wxToken + wuid).toString())) {
|
||||
return Mono.just(new AuthorizationDecision(true));
|
||||
} else {
|
||||
// token对不上
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.WECHAT_LOGIN_ERROR.getCode(), CodeMsg.WECHAT_LOGIN_ERROR.getMsg(), "token异常"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 校验权限和client状态
|
||||
|
||||
@ -20,7 +20,9 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
|
||||
/**
|
||||
* 自定义没有权限访问时的异常,没用到,因为自定义的权限校验
|
||||
* 自定义 oauth2默认权限校验 异常时的处理,
|
||||
* 没用到,因为没用默认权限校验,我们使用的自定义的权限校验
|
||||
* 自定义的权限校验 中的 异常用exception包中的全局异常处理的
|
||||
* 参考{@link org.springframework.security.oauth2.server.resource.web.access.server.BearerTokenServerAccessDeniedHandler}
|
||||
* @author zrb
|
||||
* @date 2024年9月4日10:17:27
|
||||
@ -36,7 +38,7 @@ public class MyAccessDeniedHandler implements ServerAccessDeniedHandler {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.setStatusCode(HttpStatus.OK);
|
||||
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
// 为了解决token过期时,前端不出现跨域错误,添加了一些header,注意Access-Control-Allow-Origin的值
|
||||
// 为了解决token过期时,前端不出现跨域错误,添加了一些header,注意Access-Control-Allow-Origin的值
|
||||
response.getHeaders().add("Access-Control-Allow-Origin", "*");
|
||||
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
|
||||
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
|
||||
|
||||
@ -25,8 +25,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 这个主要处理身份认证(authentication)的异常,权限验证时的异常这里是不处理的
|
||||
* 自定义Token异常,主要处理token验证过程中的异常,因token验证是oauth2的,所以要改它的异常处理响应
|
||||
* 这个主要处理身份认证(authentication)的异常
|
||||
* 权限验证时的异常不经过这个类处理
|
||||
* 参考{@link org.springframework.security.oauth2.server.resource.web.server.BearerTokenServerAuthenticationEntryPoint}
|
||||
* 重写了返回结果中的response内容:
|
||||
* 1. response的HttpStatus改成ok
|
||||
@ -55,7 +56,6 @@ public class MyAuthenticationEntryPoint implements ServerAuthenticationEntryPoin
|
||||
} else if (cause instanceof BadJwtException || cause instanceof JwtEncodingException) {
|
||||
res = new Result<String>().error(CodeMsg.TOKEN_INVALID.getCode(), CodeMsg.TOKEN_INVALID.getMsg(), cause.getMessage());
|
||||
} else if (cause instanceof InvalidBearerTokenException) {
|
||||
System.out.println("8888888");
|
||||
String token = exchange.getRequest().getHeaders().getFirst("Authorization").substring(7);
|
||||
String dateTime = DateUtil.formatDateTime(TokenUtil.getExp(token));
|
||||
res = new Result<String>().bussinessException(CodeMsg.TOKEN_EXPIRED.getCode(), CodeMsg.TOKEN_EXPIRED.getMsg(), "Jwt expired at " + dateTime);
|
||||
|
||||
@ -44,9 +44,9 @@ public class ResourceServerConfig {
|
||||
http.httpBasic(httpBasicSpec -> httpBasicSpec.disable());
|
||||
// 资源服务器配置
|
||||
http.oauth2ResourceServer(server -> server
|
||||
// 权限不通过时,自定义返回
|
||||
// 默认的权限校验方法 不通过时,自定义返回,这个用不到,因为没有走默认权限校验
|
||||
.accessDeniedHandler(new MyAccessDeniedHandler())
|
||||
// 未登录或者登陆验证失败时(token有问题),自定义返回
|
||||
// 凭据验证失败时(token有问题),自定义返回
|
||||
.authenticationEntryPoint(new MyAuthenticationEntryPoint())
|
||||
// 使用jwt默认配置
|
||||
// .jwt(Customizer.withDefaults())
|
||||
@ -66,6 +66,7 @@ public class ResourceServerConfig {
|
||||
exchange
|
||||
.pathMatchers(Convert.toStrArray(ignoreUri.getIgnoreUris())).permitAll()
|
||||
.pathMatchers(ignoreFixedUris()).permitAll()
|
||||
.pathMatchers("/gateway/wechat/*").access(authorizationManager)
|
||||
// .anyExchange().authenticated()
|
||||
// 其他走自定义逻辑
|
||||
.anyExchange().access(authorizationManager)
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
package com.evotech.hd.gateway.oauth2;
|
||||
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.web.server.authorization.AuthorizationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.evotech.hd.common.core.constant.HDConstant;
|
||||
import com.evotech.hd.common.core.enums.CodeMsg;
|
||||
import com.evotech.hd.common.core.utils.XCXUtil;
|
||||
import com.evotech.hd.common.redis.utils.RedisUtil;
|
||||
import com.evotech.hd.gateway.oauth2.service.WechatService;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 微信服务token校验
|
||||
* 鉴权时统一抛出OAuth2AuthorizationException
|
||||
*/
|
||||
@Component
|
||||
public class WechatAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
|
||||
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
@Resource
|
||||
private WechatService wechatService;
|
||||
|
||||
@Value("${yt.gateway.is_pass:false}")
|
||||
private boolean isPass;
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
|
||||
if (isPass) {
|
||||
return Mono.just(new AuthorizationDecision(true));
|
||||
}
|
||||
ServerHttpRequest request = authorizationContext.getExchange().getRequest();
|
||||
String uri = request.getURI().toString();
|
||||
|
||||
// 1. 对应跨域的预检请求直接放行
|
||||
if (request.getMethod() == HttpMethod.OPTIONS) {
|
||||
return Mono.just(new AuthorizationDecision(true));
|
||||
}
|
||||
|
||||
return wechatTokenCheck(request, uri);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 微信token权限验证逻辑
|
||||
* @param request
|
||||
* @param uri
|
||||
* @return
|
||||
*/
|
||||
private Mono<AuthorizationDecision> wechatTokenCheck(ServerHttpRequest request, String uri) {
|
||||
String wuid = request.getHeaders().getFirst(HDConstant.WECHAT_SERVER_AUTHORIZATION_KEY);
|
||||
String wxToken = request.getHeaders().getFirst(HDConstant.WECHAT_SERVER_TOKEN_KEY);
|
||||
if (!StringUtils.hasText(wxToken) || !StringUtils.hasText(wuid)) {
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.WECHAT_NOT_LOGIN.getCode(), CodeMsg.WECHAT_NOT_LOGIN.getMsg(), ""));
|
||||
}
|
||||
// 验证wuid
|
||||
if (!redisUtil.hasKey(HDConstant.openidPrefix + wuid)) {
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.WECHAT_LOGIN_ERROR.getCode(), CodeMsg.WECHAT_LOGIN_ERROR.getMsg(), uri));
|
||||
}
|
||||
// 验证token正确
|
||||
if (!redisUtil.hasKey(HDConstant.wxToken + wuid)) {
|
||||
// 缓存没了,过期了
|
||||
if (!wxToken.equals(XCXUtil.wechatToken(wuid, redisUtil.get(HDConstant.tokenRandomStr + wuid).toString()))) {
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.WECHAT_LOGIN_ERROR.getCode(), CodeMsg.WECHAT_LOGIN_ERROR.getMsg(), "token异常"));
|
||||
}
|
||||
Map<String, String> m = wechatService.tokenBuilder(wuid);
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.WECHAT_TOKEN_INVALID.getCode(), CodeMsg.WECHAT_TOKEN_INVALID.getMsg(), JSONUtil.toJsonStr(m)));
|
||||
} else {
|
||||
// 缓存还有
|
||||
if (wxToken.equals(redisUtil.get(HDConstant.wxToken + wuid).toString())) {
|
||||
return Mono.just(new AuthorizationDecision(true));
|
||||
} else {
|
||||
// token对不上
|
||||
throw new OAuth2AuthorizationException(new OAuth2Error(CodeMsg.WECHAT_LOGIN_ERROR.getCode(), CodeMsg.WECHAT_LOGIN_ERROR.getMsg(), "token异常"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -24,6 +24,7 @@ import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
* 根据{@link}ErrorWebFluxAutoConfiguration的配置 重写
|
||||
* 主要是重写errorWebExceptionHandler()的逻辑
|
||||
* 里面不要DefaultErrorWebExceptionHandler了,用自己写的异常处理类替换
|
||||
* 这个全局异常 主要用来捕获 自定义权限验证{@link AuthorizationManager}类中抛出的异常
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
//@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
|
||||
@ -22,6 +22,7 @@ import reactor.core.publisher.Mono;
|
||||
/**
|
||||
* 异常处理操作,自定义异常中的内容
|
||||
* 重写了{@link}DefaultErrorWebExceptionHandler部分内容
|
||||
* 这个全局异常 主要用来捕获 自定义权限验证{@link AuthorizationManager}类中抛出的异常
|
||||
*/
|
||||
public class GlobalExceptionHandler extends DefaultErrorWebExceptionHandler {
|
||||
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
package com.evotech.hd.gateway.oauth2.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@FeignClient(value = "wechat-server")
|
||||
public interface WechatService {
|
||||
|
||||
@PostMapping(value = "/wechat/login/tokenbuild",
|
||||
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
|
||||
public Map<String, String> tokenBuilder(@RequestParam String wuid);
|
||||
|
||||
}
|
||||
@ -1,13 +1,26 @@
|
||||
package com.evotech.hd.gateway;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.evotech.hd.gateway.oauth2.service.WechatService;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@SpringBootTest
|
||||
class GatewayServerApplicationTests {
|
||||
|
||||
@Resource
|
||||
private WechatService wechatService;
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
Map<String, String> m = wechatService.tokenBuilder("123");
|
||||
System.out.println("0000000000");
|
||||
System.out.println(m);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ public class WechatAgreementController {
|
||||
@Operation(summary = "查询")
|
||||
@GetMapping("/list")
|
||||
@ApiOperationSupport(order = 4)
|
||||
public Result<List<WechatAgreement>> list(@RequestParam String appid, @RequestParam Integer type, @RequestParam Integer status) {
|
||||
public Result<List<WechatAgreement>> list(@RequestParam(required = false) String appid, @RequestParam(required = false) Integer type, @RequestParam(required = false) Integer status) {
|
||||
return wechatAgreementService.list(appid, type, status);
|
||||
}
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ public class WechatSwiperController {
|
||||
@Operation(summary = "查询")
|
||||
@GetMapping("/list")
|
||||
@ApiOperationSupport(order = 4)
|
||||
public Result<List<WechatSwiper>> list(@RequestParam String appid, @RequestParam Integer type, @RequestParam Integer status) {
|
||||
public Result<List<WechatSwiper>> list(@RequestParam(required = false) String appid, @RequestParam(required = false) Integer type, @RequestParam(required = false) Integer status) {
|
||||
return wechatSwiperService.list(appid, type, status);
|
||||
}
|
||||
|
||||
|
||||
@ -71,10 +71,6 @@
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-json</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.evotech.hd.wechat.controller;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@ -26,14 +28,14 @@ public class LoginController {
|
||||
@Operation(summary = "登陆")
|
||||
@PostMapping("/xcxlogin")
|
||||
@Parameter(name="js_code", description = "登录时获取的 code,可通过wx.login获取", example = "")
|
||||
public Result<String> code2Session(String js_code) {
|
||||
public Result<Map<String, String>> code2Session(String js_code) {
|
||||
return loginService.code2Session(js_code);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "检验小程序登录态")
|
||||
@PostMapping("/checksessionkey")
|
||||
public Result<Boolean> checkSessionKey(String wuid) {
|
||||
public Result<Map<String, String>> checkSessionKey(String wuid) {
|
||||
return loginService.checkSessionKey(wuid);
|
||||
}
|
||||
|
||||
@ -44,5 +46,10 @@ public class LoginController {
|
||||
return loginService.phoneNumber(wuid, code);
|
||||
}
|
||||
|
||||
@PostMapping("/tokenbuild")
|
||||
public Map<String, String> tokenBuilder(String wuid) {
|
||||
return loginService.tokenBuilder(wuid);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
package com.evotech.hd.wechat.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.evotech.hd.common.core.entity.Result;
|
||||
|
||||
public interface LoginService {
|
||||
|
||||
public Result<String> code2Session(String js_code);
|
||||
public Result<Map<String, String>> code2Session(String js_code);
|
||||
|
||||
public Result<Boolean> checkSessionKey(String wuid);
|
||||
public Result<Map<String, String>> checkSessionKey(String wuid);
|
||||
|
||||
public Result<String> phoneNumber(String wuid, String code);
|
||||
|
||||
public Map<String, String> tokenBuilder(String wuid);
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
package com.evotech.hd.wechat.service.impl;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@ -11,6 +14,7 @@ import com.evotech.hd.common.core.dao.wechat.WechatUserDao;
|
||||
import com.evotech.hd.common.core.entity.Result;
|
||||
import com.evotech.hd.common.core.entity.wechat.WechatUser;
|
||||
import com.evotech.hd.common.core.enums.CodeMsg;
|
||||
import com.evotech.hd.common.core.utils.XCXUtil;
|
||||
import com.evotech.hd.common.redis.utils.RedisUtil;
|
||||
import com.evotech.hd.wechat.config.XcxProperties;
|
||||
import com.evotech.hd.wechat.service.AccessTokenService;
|
||||
@ -18,10 +22,11 @@ import com.evotech.hd.wechat.service.LoginService;
|
||||
import com.evotech.hd.wechat.utils.xcx.LoginUtil;
|
||||
import com.evotech.hd.wechat.utils.xcx.PhoneNumberUtil;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import cn.hutool.crypto.digest.HMac;
|
||||
import cn.hutool.crypto.digest.HmacAlgorithm;
|
||||
import cn.hutool.crypto.digest.MD5;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
@ -37,13 +42,21 @@ public class LoginServiceImpl implements LoginService {
|
||||
private WechatUserDao wechatUserDao;
|
||||
@Resource
|
||||
private AccessTokenService accessTokenService;
|
||||
|
||||
@Value("${hbyt.xcx.token_exp_hour:2}")
|
||||
private Integer tokenExpHour;
|
||||
|
||||
/**
|
||||
* 登陆成功后会生成本地token,返回前端,前端请求携带
|
||||
* 在gateway中对微信服务的token重加一套验证逻辑,
|
||||
* 在gateway验证时,如果是过期,会在那重新生成token返回
|
||||
*/
|
||||
@Override
|
||||
public Result<String> code2Session(String js_code) {
|
||||
public Result<Map<String, String>> code2Session(String js_code) {
|
||||
String res = LoginUtil.code2Session(xcxProperties.getAppid(), xcxProperties.getAppSecret(), js_code);
|
||||
JSONObject jo = JSONUtil.parseObj(res);
|
||||
if (StringUtils.hasText(jo.getStr("errcode"))) {
|
||||
return new Result<String>().error(CodeMsg.WECHAT_LOGIN_ERROR, jo);
|
||||
return new Result<Map<String, String>>().error(CodeMsg.WECHAT_LOGIN_ERROR, jo);
|
||||
}
|
||||
String openid = jo.getStr("openid");
|
||||
String unionid = jo.getStr("unionid");
|
||||
@ -51,7 +64,7 @@ public class LoginServiceImpl implements LoginService {
|
||||
// 是否登陆过
|
||||
WechatUser wuser = wechatUserDao.selectOne(new QueryWrapper<WechatUser>().eq("openid", openid));
|
||||
if (wuser == null) {
|
||||
String wuid = MD5.create().digestHex(xcxProperties.getAppid() + openid + "HBYT");
|
||||
String wuid = XCXUtil.wechatUid(xcxProperties.getAppid(), openid);
|
||||
wuser = new WechatUser();
|
||||
wuser.setAppid(xcxProperties.getAppid());
|
||||
wuser.setCtime(new Date());
|
||||
@ -64,25 +77,34 @@ public class LoginServiceImpl implements LoginService {
|
||||
redisUtil.set(HDConstant.openidPrefix + wuser.getWuid(), openid);
|
||||
redisUtil.set(HDConstant.unionidPrefix + wuser.getWuid(), unionid);
|
||||
redisUtil.set(HDConstant.sessionKeyPrefix + wuser.getWuid(), sessionKey);
|
||||
|
||||
return new Result<String>().success(wuser.getWuid());
|
||||
// token
|
||||
Map<String, String> m = tokenBuilder(wuser.getWuid());
|
||||
return new Result<Map<String, String>>().success(m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 检查登录态,检查成功后刷新本地token
|
||||
*/
|
||||
@Override
|
||||
public Result<Boolean> checkSessionKey(String wuid) {
|
||||
public Result<Map<String, String>> checkSessionKey(String wuid) {
|
||||
if (!redisUtil.hasKey(HDConstant.openidPrefix + wuid) || !redisUtil.hasKey(HDConstant.sessionKeyPrefix + wuid)) {
|
||||
return new Result<Boolean>().error(CodeMsg.WECHAT_SERRION_ERROR);
|
||||
return new Result<Map<String, String>>().error(CodeMsg.WECHAT_SERRION_ERROR);
|
||||
}
|
||||
String openid = redisUtil.get(HDConstant.openidPrefix + wuid).toString();
|
||||
String sessionKey = redisUtil.get(HDConstant.sessionKeyPrefix + wuid).toString();
|
||||
HMac hmac = DigestUtil.hmac(HmacAlgorithm.HmacSHA256, sessionKey.getBytes());
|
||||
String res = LoginUtil.code2Session(xcxProperties.getAppid(), openid, hmac.digestHex(""));
|
||||
String res = LoginUtil.checkSessionKey(accessTokenService.getAccessToken(), openid, hmac.digestHex(""));
|
||||
JSONObject jo = JSONUtil.parseObj(res);
|
||||
if (jo.getInt("errcode") == 0) {
|
||||
return new Result<Boolean>().success("OK");
|
||||
Map<String, String> m = tokenBuilder(wuid);
|
||||
return new Result<Map<String, String>>().success(m);
|
||||
}
|
||||
return new Result<Boolean>().error(CodeMsg.WECHAT_SERRION_ERROR, jo);
|
||||
return new Result<Map<String, String>>().error(CodeMsg.WECHAT_SERRION_ERROR, jo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Result<String> phoneNumber(String wuid, String code) {
|
||||
@ -106,6 +128,19 @@ public class LoginServiceImpl implements LoginService {
|
||||
return new Result<String>().error(CodeMsg.WECHAT_API_ERROR, jo);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, String> tokenBuilder(String wuid) {
|
||||
String randomStr = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_FORMATTER);
|
||||
String wxToken = XCXUtil.wechatToken(wuid, randomStr);
|
||||
redisUtil.set(HDConstant.wxToken + wuid, wxToken, tokenExpHour * 3600L);
|
||||
redisUtil.set(HDConstant.tokenRandomStr + wuid, randomStr);
|
||||
Map<String, String> m = new HashMap<String, String>();
|
||||
m.put("wuid", wuid);
|
||||
m.put("wxToken", wxToken);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -38,5 +38,10 @@
|
||||
"name": "hbyt.xcx.refund_notify_url",
|
||||
"type": "java.lang.String",
|
||||
"description": "A description for 'hbyt.xcx.refund_notify_url'"
|
||||
},
|
||||
{
|
||||
"name": "hbyt.xcx.token_exp_hour",
|
||||
"type": "java.lang.String",
|
||||
"description": "A description for 'hbyt.xcx.token_exp_hour'"
|
||||
}
|
||||
]}
|
||||
@ -55,4 +55,7 @@ hbyt:
|
||||
# 支付回调地址
|
||||
notify_url: https://api.evo-techina.com/wechat/wechatpay/prepayback/msg
|
||||
# 退款回调地址
|
||||
refund_notify_url: https://api.evo-techina.com/wechat/wechatpay/refundsback/msg
|
||||
refund_notify_url: https://api.evo-techina.com/wechat/wechatpay/refundsback/msg
|
||||
# token缓存时效:小时
|
||||
token_exp_hour: 2
|
||||
|
||||
Loading…
Reference in New Issue
Block a user