codecow

文:公众号授权获取用户信息及jwt登录

  ​最近有个小伙伴问了我关于"公众号授权及JWT登录"问题,小编也是在空闲之余,坚持不懈,写下此文,和大家分享和学习 ↓↓↓

前言

  什么是微信公众号授权登录?微信授权又是什么?jwt 怎么和微信授权结合使用?小编也不多BB 结合实际开发和大家聊聊↓↓↓

什么是微信小程序授权 ?

官网:
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

 小编就不多BB先来张图 压压惊

授权及JWT实操 

1、controller层 

@Slf4j
@RestController
@RequestMapping("/OAuth")
public class OAuthController {

    @Resource
    private OAuthService oAuthService; //授权service层接口

    @Resource
    private HttpServletRequest request;

    @Resource
    private HttpServletResponse response;

    @Resource
    private WeChatAuthorizeUtil weChatAuthorizeUtil; //微信授权工具类, 后面有

    /**
    *  第一步:前段调用此方法,当用户点击同意授权后,
    *           小编直接让他跳转调下面那个方法(/getOAuth)来获取网页授权access_token
    *    ★★★:redirect_uri 授权后重定向的回调链接地址
    *            这个值填写的是下面那个方法的url
    */
    @ApiOperation(value = "授权登录", response = String.class)
    @GetMapping(value = "/getCode")
    public void loginInit() {
        try {
            response.sendRedirect(weChatAuthorizeUtil.getOAuthCodeUrl_USER()); 
        } catch (UnsupportedEncodingException e) {
            log.error("重定向url编码失败:>> " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            log.error(" response重定向失败:>> " + e.getMessage());
        }
    }

    @GetMapping(value = "/getOAuth")
    public void getOAuth() {
        try {        //此处直接获取请求的code,因为当用户点击授权后,会跳转到授权后重定向地址,也就是这个方法,从而携带过来code
            oAuthService.getWXUserInfoOAuth(request.getParameter(ColumnName.CODE));
        } catch (Exception e) {
            log.error("getOAuth error: ", e);
        }
    }
}
2、service层
public interface OAuthService {

    void getWXUserInfoOAuth(String code); //授权获取用户信息接口
    
}
3、serviceImpl层
@Service
@Slf4j
public class OAuthServiceImpl implements OAuthService {

    @Resource
    private WeChatAuthorizeUtil weChatAuthorizeUtil; //授权工具类

    @Resource
    private UserMapper userMapper; //userMapper层

    @Resource
    private CommonTemplateService templateService; //公共service层

    @Resource
    private JwtUtils jwtUtils;  //jwt 工具类

    @Resource
    private HttpServletResponse response;

    @Resource
    private WechatConfig wechatConfig; //微信配置文件

    @Override
    public void getWXUserInfoOAuth(String code) {
        try {
            if (StringUtils.isBlank(code)) {     //判断请求 code 是否为空
                log.error(" CODE_NOT_EMPTY_ERROR : << {} >>", RespResultEnum.CODE_NOT_EMPTY_ERROR.getMessage());
            }
            UserRegisterRespDTO registerRespDTO = null; //自己封装的(后面有),返回给前段的数据(包含jwt中加密的token,和用户的信息)
            ResponseEntity<String> responseEntity =     //使用resttemplate向微信提供的url(通过code换取网页授权access_token url)发送请求
                    templateService.getForEntity(weChatAuthorizeUtil.getAccessTokenUrl(code));
            if (responseEntity.getStatusCode().value() != HttpStatus.OK.value()) { //判断请求是否成功
                log.error(" VISIT_WEIXIN_API_ERROR : << {} >>", RespResultEnum.VISIT_WEIXIN_API_ERROR.getMessage()
                        + responseEntity.getStatusCodeValue());
            }
            WeiXinOAuthRespDTO weiXinOAuthRespDTO =    //获取微信“通过code换取网页授权access_token”返回的信息,并封装到自己的DTO(下面有)中
                    JSON.parseObject(responseEntity.getBody(), WeiXinOAuthRespDTO.class);
            log.info("weiXinOAuthRespDTO :" + weiXinOAuthRespDTO.toString()); //日志打印
            if (StringUtils.isBlank(weiXinOAuthRespDTO.getOpenid())) { //判断返回的openID是否为空
                log.error(" WEIXIN_CODE_ERROR : << {} >>", RespResultEnum.WEIXIN_CODE_ERROR.getMessage());
            }
            ResponseEntity<String> responseUserInfo = //使用resttemplate向微信提供的url(拉取用户信息 url)发送请求
                    templateService.getForEntity(weChatAuthorizeUtil.getUserInfoUrl(weiXinOAuthRespDTO.
                            getAccess_token(), weiXinOAuthRespDTO.getOpenid()));
            if (responseEntity.getStatusCode().value() != HttpStatus.OK.value()) { //判断请求是否成功
                log.error(" VISIT_WEIXIN_API_ERROR : << {} >>", RespResultEnum.VISIT_WEIXIN_API_ERROR.getMessage()
                        + responseEntity.getStatusCodeValue());
            }
            WeiXinOAuthUserInfoRespDTO userInfoRespDTO =  //获取微信“拉取用户信息”返回的信息,并封装到自己的DTO中(此dto包含用户头像,昵称等)
                    JSON.parseObject(responseUserInfo.getBody(), WeiXinOAuthUserInfoRespDTO.class);
            if (StringUtils.isBlank(userInfoRespDTO.getOpenid())) {
                log.error(" WEIXIN_USER_INFO_ERROR : << {} >>", RespResultEnum.WEIXIN_USER_INFO_ERROR.getMessage());
            }
            log.info("weiXinOAuth user info:" + userInfoRespDTO.toString());
            UserDO userDO = getUserByOpenId(userInfoRespDTO.getOpenid()); //此方法在下面
            if (userDO != null ) { // == null 说明 该用户第一次授权(此时:我们需要做两点==> 1、把用户信息保存到数据库;2、给前段签发token)
                //用户数据保存 数据库(省略。。。)
                //下面通过jwt向前段签发token(两个参数:1、userId,2、用户角色)
                String token = jwtUtils.generateJwt(userDO.getUserId(),
                        String.valueOf(UserTypeEnum.WEI_XIN_USER.getUserType()));
                registerRespDTO = respDTO(userDO, token);
                // ★★★ 由于项目含有不在公众号中的登录方式:因此,做了一下的从定向,(如果你项目自有公众号,到上面那一步就结束了,也就是说直接返回 registerRespDTO 就行,其中包含用户信息,以及token)
                response.sendRedirect(JumpUrlUtil.getValidUrl(WeiXinOAuthColumn.JUMP_MAIN_METHOD,
                        wechatConfig.getJumpUri(), JumpUrlUtil.transBeanToMap(registerRespDTO)));
                return;
            }
            // ★★★ 此处说明用户 不是第一次授权,数据库已经有用户信息,直接签发token跳转页面(若自有工作号,直接返回,不用从定向)
            response.sendRedirect(JumpUrlUtil.getValidUrl(WeiXinOAuthColumn.JUMP_LOGIN_METHOD,
                    wechatConfig.getJumpUri(), JumpUrlUtil.transBeanToMap(userInfoRespDTO)));
            return;
        } catch (Exception e) {
            log.error(" getWXUserInfoOAuth error: " + e.getMessage());
        }

    /**
    *    根据openid查询用户数据库DO(userDO)
    */
    private UserDO getUserByOpenId(String openId) {
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(ColumnName.OPEN_ID, openId);
        return userMapper.selectOne(queryWrapper);
        }
    
    }
4、WeChatAuthorizeUtil
@Slf4j
@Component
public class WeChatAuthorizeUtil {

    private static String ENCODE = "UTF-8";

    @Resource
    private WechatConfig wechatConfig;

    // 微信授权 用户无感知 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(也就是需要用户点击授权才行)
    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);
    }

    // 微信授权 获取用户openID url( 也就是:通过code换取网页授权access_token)
    public String getAccessTokenUrl(String code) {
        return WeiXinOAuthColumn.OAUTH_ACCESS_TOKEN_URI
                .replace("APPID", wechatConfig.getAppId())
                .replace("SECRET", wechatConfig.getSecretKey())
                .replace("CODE", code);
    }

    // 微信授权 获取用户信息( 也就是:拉取用户信息(需scope为 snsapi_userinfo))
    public String getUserInfoUrl(String accessToken, String openid) {
        return WeiXinOAuthColumn.OAUTH_USER_INFO_URI
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openid);
    }
5、WeiXinOAuthRespDTO
/**
*    微信授权:调用通过code换取网页授权access_token 的url, 对返回参数封装成自己的DTO
*/
@Data
public class WeiXinOAuthRespDTO {

    private String access_token;  //网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
    private String expires_in;    //access_token接口调用凭证超时时间,单位(秒)
    private String refresh_token; //用户刷新access_token
    private String openid;        //用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
    private String scope;         //用户授权的作用域,使用逗号(,)分隔

}
6、WeiXinOAuthUserInfoRespDTO
/**
*    微信授权:调用 拉取用户信息(需scope为 snsapi_userinfo)的url, 对返回参数封装成自己的DTO
*/
@Data
public class WeiXinOAuthUserInfoRespDTO {

    private String openid;
    private String nickname;   //用户昵称
    private String sex;        //用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
    private String province;   //用户个人资料填写的省份
    private String city;       //普通用户个人资料填写的城市
    private String country;    //国家,如中国为CN
    private String headimgurl; //用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
    private String privilege;  //用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
    private String unionid;    //只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段

}
7、JwtUtils
@Slf4j
@Component
public class JwtUtils {

    private JwtUtils() {

    }

    @Resource
    private JwtConfig jwtConfig;

    /**
     * 加密
     * @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;
        }
    }

 岁月匆忙,偷得浮生半日闲——凌晨之余写下此文

推荐

==>:微信小程序授权 + 获取用户手机号 + jwt登录

最后

若有需要源码的小伙伴,可以扫描下面公众号,回复:"公众号授权及JWT登录"小编直接发"百度网盘"↓↓↓
也可以加小编微信:CodeCow-6666 私信小编,切记:不懂一定要问

 

 

 

 

 

 

 

分类:

技术点:

相关文章: