文:java微信小程序授权+获取用户手机号 + jwt登录 !!!
继上一篇 "公众号授权获取用户信息及jwt登录",又有小伙伴私聊小编,小程序授权+获取用户手机号 + jwt登录有什么区别,空闲之余,写下此文↓↓↓
前言
上篇文章《公众号授权获取用户信息及jwt登录》小编以实操+源代码的方式和伙伴们演示了公众号授权,但却遗留了一个问题,公众号授权和小程序授权到底有什么区别?
什么是微信小程序授权 ?
官网:
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
小程序授能干嘛 ?
小编就不多BB先来张图 压压惊
这图不难理解:
当我们进入任何一个小程序时,都会遇到的常规操作,授权自己信息、以及申请使用自己手机号。
但是:
上图界面具体是怎么实现的呢?
别急,小编这就带大家浅聊微信小程序授权~~
小程序授权、手机号及JWT实操
1、controller层
@Slf4j @CrossOrigin @RestController @RequestMapping("/operation") public class RefuelManagerOperationController { @Resource private OAuthService oAuthService; //service层 /** * 第一步:获取会话秘钥(session_key),给前段返回openID
*/ @ApiOperation(value = "授权获取会话秘钥", response = Code2SessionRespDTO.class) @ApiResponses({@ApiResponse(code = 0, message = "Success"), @ApiResponse(code = 500, message = "failed")}) @PostMapping(value = "/getSessionKey", consumes = "application/json", produces = "application/json") public RespResult<Code2SessionRespDTO> getWXOAuthCode2Session(@RequestBody @Valid GetWXOAuthCode2SessionReqDTO getWXOAuthCode2SessionReqDTO){ try { return oAuthService.getWXOAuthCode2Session(getWXOAuthCode2SessionReqDTO.getCode()); } catch (Exception e) { log.error("getWXOAuthCode2Session error: ", e); return RespResult.error(RespResultEnum.ERROR.getCode(), e.getMessage()); } } /** * 第二步:根据加密数据、初始向量 获取用户手机号
*/ @ApiOperation(value = "授权获取用户手机号", response = UserInfoDTO.class) @ApiResponses({@ApiResponse(code = 0, message = "Success"), @ApiResponse(code = 500, message = "failed")}) @PostMapping(value = "/getUserMobile", consumes = "application/json", produces = "application/json") public RespResult<UserInfoDTO> getUserMobile(@RequestBody @Valid WeiXinOAuthGetUserMobileReqDTO weiXinOAuthGetUserMobileReqDTO) { try { //调用service层 return oAuthService.getUserMobile(weiXinOAuthGetUserMobileReqDTO); } catch (Exception e) { log.error("getUserMobile error: ", e); return RespResult.error(RespResultEnum.ERROR); } }
2、service层
public interface OAuthService {
RespResult<Code2SessionRespDTO> getWXOAuthCode2Session(String js_code);
RespResult<UserInfoDTO> getUserMobile(WeiXinOAuthGetUserMobileReqDTO weiXinOAuthGetUserMobileReqDTO);
}
3、serviceImpl层
/** * Create By CodeCow on 2020/6/29. */ @Slf4j @Service public class OAuthServiceImpl implements OAuthService { @Resource private CommonRestTemplateService templateService; //自己封装的restTemplate(在最后) @Resource private WeChatAuthorizeUtil weChatAuthorizeUtil;//授权工具类(在最后) @Resource private RefuelUserMapper refuelUserMapper;// 用户mapper层 @Resource private JwtUtils jwtUtils; //jwt工具类(在最后) @Resource private RedisProperties redisProperties; //redis 配置(在最后) @Resource private RedisTemplate redisTemplate; //redisTemplate(在最后) @Resource private HttpServletResponse response; @Override public RespResult<Code2SessionRespDTO> getWXOAuthCode2Session(String js_code) { //new一个前段返回对象 Code2SessionRespDTO respDTO = new Code2SessionRespDTO(); //使用restTemplate调用调用微信提供的接口(auth.code2Session接口) //从而获取 用户唯一标识 OpenID 和 会话密钥 session_key。 ResponseEntity<String> responseEntity = templateService.getForEntity(weChatAuthorizeUtil.getCode2SessionUrl(js_code)); //判断请求是否成功 if (responseEntity.getStatusCode().value() != HttpStatus.OK.value()) { return RespResult.error(RespResultEnum.JS_CODE_REQ_ERROR); } //获取微信“auth.code2Session接口”返回的信息,并封装到自己的DTO中(DTO在最后) WeiXinOAuthCode2SessionRespDTO code2SessionRespDTO = JSON.parseObject(responseEntity.getBody(), WeiXinOAuthCode2SessionRespDTO.class); //判断微信返回的用户openID是否为空 if (StringUtils.isBlank(code2SessionRespDTO.getOpenid())) { return RespResult.error(RespResultEnum.SESSION_KEY_ERROR.getCode(), RespResultEnum.SESSION_KEY_ERROR.getmsg() + code2SessionRespDTO.getErrmsg()); } //把从微信获取到的 会话密钥 session_key 放到redis中 //(因为微信官方特别提醒:为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。) redisTemplate.opsForValue().set(redisProperties.getSessionKey() + code2SessionRespDTO.getOpenid(), code2SessionRespDTO.getSession_key()); //根据用户openID查询数据库(在下面) RefuelUserDO userDO = getUserByOpenId(code2SessionRespDTO.getOpenid()); //为 null,说明数据库没有当前授权人的信息(说明该用户第一次授权) if( userDO == null){ respDTO.setIsMiniUser(UserLoginMarkEnum.FIRST_LOGIN.getLoginMark()); }else { //否则 用户不是第一次授权(说明已经是该小程序用户) respDTO.setIsMiniUser(UserLoginMarkEnum.LAST_LOGIN.getLoginMark()); //直接使用 jwt 签发 token String token = jwtUtils.generateJwt(userDO.getUserId(), UserTypeEnum.WX_USER.getUserType()); UserInfoDTO userInfoDTO = new UserInfoDTO(); BeanUtils.copyProperties(userDO,userInfoDTO); userInfoDTO.setToken(token); userInfoDTO.setUserId(String.valueOf(userDO.getUserId())); userInfoDTO.setCreateTime(String.valueOf(userDO.getCreateTime().getTime())); respDTO.setUserInfo(userInfoDTO); } respDTO.setOpenid(code2SessionRespDTO.getOpenid()); return RespResult.success(respDTO); } @Override public RespResult<UserInfoDTO> getUserMobile(WeiXinOAuthGetUserMobileReqDTO reqDTO) { UserInfoDTO respDTO = new UserInfoDTO(); //使用restTemplate调用调用微信提供的接口(获取手机号接口) //从而获取 用户手机号 WeiXinOAuthUserInfoRespDTO weiXinUserInfo = JSON.parseObject(reqDTO.getUserInfo(), WeiXinOAuthUserInfoRespDTO.class); //从 redis 获取上一步存 的 会话密钥 session_key String sessionKey = (String) redisTemplate.opsForValue(). get(redisProperties.getSessionKey() + weiXinUserInfo.getOpenid()); //根据前段传的 加密数据(encryptedData)、初始向量(iv) // 和 redis中的 会话密钥 session_key 三者来解密获得用户手机号(解密util在最后) String mobileStr = WeChatMobileDecryptUtil.mobileDecrypt(sessionKey, reqDTO.getIv(), reqDTO.getEncryptedData()); if (mobileStr == null) { return RespResult.error(RespResultEnum.DECRYPT_MOBILE_ERROR); } //获取微信“获取手机号接口”返回的信息,并封装到自己的DTO中 WeiXinOAuthGetUserMobileRespDTO mobileInfo = JSON.parseObject(mobileStr, WeiXinOAuthGetUserMobileRespDTO.class); //根据当前用户手机号查询数据库(因为小编项目含有APP登录,所以的验证该手机是否通过手机号注册过) // 若“你们”只有小程序此步省略 RefuelUserDO userInfo = getUserByMobile(mobileInfo.getPhoneNumber()); RefuelUserDO refuelUserDO; // == null 说明,该用户没有APP注册过 if (userInfo == null) { refuelUserDO = insertRefuelUser(weiXinUserInfo, mobileInfo); //插入用户信息 }else { // 用户 通过APP注册过 refuelUserDO = updateRefuelUser(userInfo, weiXinUserInfo);//更新用户信息 } BeanUtils.copyProperties(refuelUserDO, respDTO); respDTO.setUserId(String.valueOf(refuelUserDO.getUserId())); respDTO.setCreateTime(String.valueOf(refuelUserDO.getCreateTime().getTime())); //直接使用 jwt 签发 token String token = jwtUtils.generateJwt(refuelUserDO.getUserId(), UserTypeEnum.WX_USER.getUserType()); respDTO.setToken(token); return RespResult.success(respDTO); } //根据openID查询用户DO private RefuelUserDO getUserByOpenId(String openId) { QueryWrapper<RefuelUserDO> queryWrapper = new QueryWrapper<>(); queryWrapper.eq(ColumnName.OPEN_ID, openId); return refuelUserMapper.selectOne(queryWrapper); } //根据手机号(mobile)查询用户DO private RefuelUserDO getUserByMobile(String mobile) { QueryWrapper<RefuelUserDO> queryWrapper = new QueryWrapper<>(); queryWrapper.eq(ColumnName.MOBILE, mobile); return refuelUserMapper.selectOne(queryWrapper); }
4、WeChatAuthorizeUtil
@Slf4j @Component public class WeChatAuthorizeUtil { private WeChatAuthorizeUtil(){ } @Resource private WechatConfig wechatConfig; //调用 auth.code2Session 接口(也就是下面的URL),换取 用户唯一标识 OpenID 和 会话密钥 session_key。 public String getCode2SessionUrl(String js_code) { return WeiXinOAuthColumn.OAUTH_CODE2SESSION_URI .replace("APPID", wechatConfig.getAppId()) .replace("SECRET", wechatConfig.getSecretKey()) .replace("JSCODE", js_code); } // 此为公众号授权url(小编上篇文章有讲过公众号授权)链接==>:公众号授权获取用户信息及jwt登录 // 微信授权 用户无感知 url(也就是用户不用点授权也能获取openID(不包含昵称、地区等),前提:在公众号内) public String getOAuthCodeUrl_BASE() throws UnsupportedEncodingException { return WeiXinOAuthColumn.OAUTH_CODE_URI .replace("APPID", wechatConfig.getAppId()) .replace("REDIRECT_URI", URLEncoder.encode(wechatConfig.getFirstCodeRedirectUrl(), ENCODE)) .replace("SCOPE", WeiXinOAuthColumn.SNSAPI_BASE) .replace("STATE", WeiXinOAuthColumn.STATE); } // 此为公众号授权url(小编上篇文章有讲过公众号授权)链接==>:公众号授权获取用户信息及jwt登录 // 微信授权 用户有感知 url(也就是需要用户点击授权才行) public String getOAuthCodeUrl_USER() throws UnsupportedEncodingException { return WeiXinOAuthColumn.OAUTH_CODE_URI .replace("APPID", wechatConfig.getAppId()) .replace("REDIRECT_URI", URLEncoder.encode(wechatConfig.getLoginCodeRedirectUrl(), ENCODE)) .replace("SCOPE", WeiXinOAuthColumn.SNSAPI_USERINFO) .replace("STATE", WeiXinOAuthColumn.STATE); }
5、JwtUtils
// jwt 加密和解密工具类 @Slf4j @Component public class JwtUtils { private JwtUtils() { } @Resource private JwtConfig jwtConfig; // jwt 配置(加签 和 过期时间) /** * 加密 * @param userId * @return */ public String generateJwt(Long userId, String userType) { byte[] encode = Base64.getEncoder().encode(jwtConfig.getSecurityKey().getBytes()); Map<String, Object> claims = Maps.newHashMap(); claims.put(ColumnName.USER_ID, userId); //用户userId claims.put(ColumnName.USER_TYPE, userType); //用户角色 return Jwts.builder() .addClaims(claims) //内容 .setIssuedAt(TimeUtil.now()) //发行时间 .setExpiration(TimeUtil.getExpiration(jwtConfig.getOutTime())) //超时时间 .signWith(SignatureAlgorithm.HS512, encode) .compact(); } /** * 解密 * @param token * @return */ public Claims extractJwt(String token) { try { byte[] encode = Base64.getEncoder().encode(jwtConfig.getSecurityKey().getBytes()); return Jwts.parser() .setSigningKey(encode) .parseClaimsJws(token) .getBody(); } catch (Exception ex) { log.error("parseJWT error for {}", token); return null; } }
6、Code2SessionRespDTO
// 授权获取会话秘钥 返回给前段DTO @Data public class Code2SessionRespDTO { @ApiModelProperty(value = "0-不是,1-是(是否为小程序用户)", required = true) private String isMiniUser; @ApiModelProperty(value = "openid", required = true) private String openid; @ApiModelProperty(value = "用户信息(isMiniUser=1时有数据)", required = false) private UserInfoDTO userInfo; }
7、WeiXinOAuthGetUserMobileReqDTO
// 获取用户手机号 请求DTO @Data public class WeiXinOAuthGetUserMobileReqDTO { @NotEmpty @ApiModelProperty(value = "完整用户信息的加密数据", required = true) private String encryptedData; @NotEmpty @ApiModelProperty(value = "加密算法的初始向量", required = true) private String iv; @ApiModelProperty(value = "敏感数据对应的云 ID", required = false) private String cloudID; @ApiModelProperty(value = "用户信息", required = true) private String userInfo; }
生活如海,宽容作舟,泛舟于海,方知海之宽阔 —— 凌晨之余写下此文!!
推荐
==>:公众号授权获取用户信息及jwt登录
最后
若有需要源码的小伙伴,可以扫描下面公众号,回复:"小程序授权及JWT登录"小编直接发"百度网盘"↓↓↓
也可以加小编微信:CodeCow-6666 私信小编,切记:不懂一定要问