通过controller层调用service层的login方法,service层代码详解如下:
1、前端获取code通过http get请求传给后端
2、后端根据微信的秘钥解析code获取openid等信息
3、解析成功返回jwt的token给前端,微信小程序前端能够通过jwt token调用后台的业务接口
Service层代码如下:
public BaseResponse login(String code) { if (StringUtils.isBlank(code)) { return BaseResponse.error("code is required"); } JSONObject resJson = weChatManager.getSessionKeyOrOpenId(code); long now = System.currentTimeMillis(); long expireAt = now + BaseConstants.EXPIRE * 1000; resJson.put(StringConstants.EXPIRE_AT, expireAt); resJson.put("accountType","wechat"); log.debug("resJson code:{}", resJson); Integer errcode = resJson.getInteger("errcode"); String openid = resJson.getString("openid"); String sessionKey = resJson.getString("session_key"); try { if (Objects.isNull(errcode)) { if (StringUtils.isBlank(openid)) { return BaseResponse.error("openid is blank from wechat"); } Map<String, String> map = Maps.newHashMap(); map.put("openid", openid); map.put("accountType","wechat"); String token = JWTUtil.generateToken(resJson, BaseConstants.PRIVATE_KEY, BaseConstants.SIGNER_KEY); map.put("token", token); map.put("sessionKey", sessionKey); return BaseResponse.success(map); } switch (errcode) { case 0: Map<String, String> map = Maps.newHashMap(); map.put("openid", openid); String token = JWTUtil.generateToken(resJson, BaseConstants.PRIVATE_KEY, BaseConstants.SIGNER_KEY); map.put("token", token); map.put("sessionKey", sessionKey); return BaseResponse.success(map); default: { return BaseResponse.error("登录失败"); } } } catch (AuthException e) { return BaseResponse.error("登录异常"); }}
Manager层代码如下
public JSONObject getSessionKeyOrOpenId(String code) { JSONObject jsonObject = new JSONObject(); String requestUrl = BaseConstants.WECHAT_URL; Map<String, String> requestUrlParam = new HashMap<>(); // TODO 小程序appId requestUrlParam.put("appid", BaseConstants.APP_ID); // TODO 小程序secret requestUrlParam.put("secret", BaseConstants.SECRET); //小程序端返回的code requestUrlParam.put("js_code", code); //默认参数 requestUrlParam.put("grant_type", "authorization_code"); //发送post请求读取调用微信接口获取openid用户唯一标识 jsonObject = JSON.parseObject(HttpClientUtil.doPost(requestUrl, requestUrlParam)); return jsonObject;}
BaseResponse公共返回参数:
@Data@AllArgsConstructorpublic class BaseResponse<T> { int code; String message; T data; public static <T> BaseResponse<T> of(T data) { return new BaseResponse<>(200, "OK", data); } public static <T> BaseResponse<T> bad(int code, String message) { return new BaseResponse<>(code, message, null); } public static <T> BaseResponse<T> success(T data) { return new BaseResponse<>(200, "OK", data); } public static <T> BaseResponse<T> success() { return new BaseResponse<>(200, "OK",null); } public static <T> BaseResponse<T> error(int code, String message) { return new BaseResponse<>(code, message, null); } public static <T> BaseResponse<T> error( String message) { return new BaseResponse<>(999, message, null); } public static <T> BaseResponse<T> error( int code, String message,T data) { return new BaseResponse<>(code, message, data); }}
JWTUtil工具类:
public class JWTUtil { public static JSONObject checkIn(String token, String privateKey, String signerKey) throws AuthException { JSONObject data; try { JWEObject jweObject = JWEObject.parse(token); jweObject.decrypt(new DirectDecrypter(privateKey.getBytes())); JWSObject jwsObject = jweObject.getPayload().toJWSObject(); JWSVerifier verifier = new MACVerifier(signerKey.getBytes()); if (!jwsObject.verify(verifier)) { log.error("Couldn't parse JWS object: invalid sign!"); throw new AuthException(); } data = JSONObject.parseObject(jwsObject.getPayload().toString()); if (log.isDebugEnabled()) { log.debug("check token value:{}", data); } return data; } catch (Exception e) { log.error("auth check token error:", e); throw new AuthException(); } } public static JSONObject check(String token, String privateKey, String signerKey) throws AuthException { JSONObject data = checkIn(token, privateKey, signerKey); if (data.getLongValue(StringConstants.EXPIRE_AT) <= System.currentTimeMillis()) { log.info("token expired:{}", token); throw new AuthException(); } return data; } public static String generateToken(JSONObject value, String privateKey, String signerKey) throws AuthException { Payload payload = new Payload(value.toJSONString()); JWSHeader headerJWS = new JWSHeader(JWSAlgorithm.HS256); JWSObject jwsObject = new JWSObject(headerJWS, payload); try { jwsObject.sign(new MACSigner(signerKey.getBytes())); JWEHeader header = new JWEHeader(JWEAlgorithm.DIR, EncryptionMethod.A128GCM); JWEObject jweObject = new JWEObject(header, new Payload(jwsObject)); if (log.isDebugEnabled()) { log.debug("generateToken info:{}", value); } jweObject.encrypt(new DirectEncrypter(privateKey.getBytes())); return jweObject.serialize(); } catch (JOSEException e) { log.error("generate token failed: {}", e); throw new AuthException(); } }}
AuthException异常类:
public class AuthException extends Exception { private ExceptionStatus status; public AuthException() { super(); this.status = ExceptionStatus.UNKNOWN; } public AuthException(ExceptionStatus status) { super(); this.status = status; }}
BaseConstants用作常量存放
public interface BaseConstants { @ApiModelProperty("微信小程序登录url") String WECHAT_URL = "https://api.weixin.qq.com/sns/jscode2session"; @ApiModelProperty("微信小程序appId") String APP_ID = "*************"; @ApiModelProperty("微信小程序密钥secret") String SECRET = ""; @ApiModelProperty("过期时间") Long EXPIRE = 259200L; @ApiModelProperty("JWT登录用") String PRIVATE_KEY = "*************"; @ApiModelProperty("JWT登录用") String SIGNER_KEY= "*************";}