1. 完善小程序token验证

2. gateway服务添加openfein实现服务调用
3. 统一小程序uid和token生成方法
This commit is contained in:
Administrator 2024-12-31 08:48:11 +08:00
parent 9354169741
commit 4fadcb5b8e
24 changed files with 325 additions and 38 deletions

View File

@ -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>

View File

@ -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:";
}

View File

@ -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;

View File

@ -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");
}
}

View File

@ -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>

View File

@ -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) {

View File

@ -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()));
}
}

View File

@ -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状态

View File

@ -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");

View File

@ -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);

View File

@ -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)

View File

@ -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异常"));
}
}
}
}

View File

@ -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)

View File

@ -22,6 +22,7 @@ import reactor.core.publisher.Mono;
/**
* 异常处理操作自定义异常中的内容
* 重写了{@link}DefaultErrorWebExceptionHandler部分内容
* 这个全局异常 主要用来捕获 自定义权限验证{@link AuthorizationManager}类中抛出的异常
*/
public class GlobalExceptionHandler extends DefaultErrorWebExceptionHandler {

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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'"
}
]}

View File

@ -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