首先呢,"登录","授权","授权登录", 是一样的意思, 不用纠结.
写小程序授权登录的代码前, 需要了解清楚 openid 与 unionid 的区别, 这里再简单介绍一下:
腾讯有个 "微信. 开放平台", 只有企业才能注册账号, 可理解为微信体系里, 最顶级的账号. 官网地址: https://open.weixin.qq.com
除了这个微信开放平台, 还有另一个叫做 "微信公众平台", 可注册四种账号, 包括服务号, 订阅号, 小程序, 企业微信. 也就是说, 公众号 (服务号和订阅号可统称为公众号) 占一个账号, 小程序也占一个账号. 在没有绑定开放平台前, 小程序授权登录只能拿到用户的 openid. 官网地址: https://mp.weixin.qq.com
小程序可绑定在公众号下, 公众号可以绑定在微信开放平台下, 小程序也可以绑定在微信开放平台下.(好像有点小绕)简单点说, 所有的公众平台账号都需要绑定在 "开放平台" 下, 才可获得的 unionid, 这是打通同个企业下所有微信公众账号的最有效方法(官方推荐)
更加具体的可自行百度...
一, 以下为小程序登录的代码:
方式一: 通过 code 调用 code2session 接口获得 message, 包含 openid,session_key, 满足条件的情况下还能直接获得 unionid
条件如下:(存在局限性)
官方说明 UnionID 获取途径, 如果开发者帐号下存在同主体的公众号, 并且该用户已经关注了该公众号. 开发者可以直接通过 + 获取到该用户 UnionID, 无须用户再次授权.
开发者帐号下存在同主体的公众号或移动应用, 并且该用户已经授权登录过该公众号或移动应用. 也可通过 code2session 获取该用户的 UnionID.
- /**
- * Author: huanglp
- * Date: 2018-11-28
- */
- public class WeiXinUtils {
- private static Logger log = LoggerFactory.getLogger(WeiXinUtils.class);
- /**
- * 通过前端传过来的 code, 调用小程序登录接口, 获取到 message 并返回 (包含 openid session_key 等)
- *
- * @param code
- * @return
- */
- public static JSONObject login(String code) {
- log.info("============== 小程序登录方法开始 ================");
- WxMiniProperties properties = WeiXinPropertiesUtils.getWxMiniProperties();
- String url = properties.getInterfaceUrl() + "/sns/jscode2session?appid="
- + properties.getAppId() + "&secret=" + properties.getAppSecret()
- + "&js_code=" + code + "&grant_type=authorization_code";
- JSONObject message;
- try {
- // RestTemplate 是 Spring 封装好的, 挺好用, 可做成单例模式
- RestTemplate restTemplate = new RestTemplate();
- String response = restTemplate.getForObject(url, String.class);
- message = JSON.parseObject(response);
- } catch (Exception e) {
- log.error("微信服务器请求错误", e);
- message = new JSONObject();
- }
- log.info("message:" + message.toString());
- log.info("============== 小程序登录方法结束 ================");
- return message;
- // 后续, 可获取 openid session_key 等数据, 以下代码一般放在 Service 层
- //if (message.get("errcode") != null) {
- // throw new ValidationException(message.toString());
- //}
- //String openid = message.get("openid").toString();
- //String sessionKey = message.get("session_key").toString();
- //...
- }
- }
- 补充 1: WeiXinPropertiesUtils 工具类
- public class WeiXinPropertiesUtils {
- // 微信小程序配置
- private static WxMiniProperties miniProperties;
- // 微信公众号配置
- private static WxProperties wxProperties;
- private static void init() {
- if (miniProperties == null) {
- miniProperties = ContextLoader.getCurrentwebApplicationContext()
- .getBean(WxMiniProperties.class);
- }
- if (wxProperties == null) {
- wxProperties = ContextLoader.getCurrentWebApplicationContext()
- .getBean(WxProperties.class);
- }
- }
- public static WxMiniProperties getWxMiniProperties() {
- init();
- return miniProperties;
- }
- public static WxProperties getWxProperties() {
- init();
- return wxProperties;
- }
- }
- 补充 2: WxMiniProperties 配置类
- @Data
- @Component
- @ConfigurationProperties(prefix = "luwei.module.wx-mini")
- public class WxMiniProperties {
- private String appId;
- private String appSecret;
- private String interfaceUrl;
- }
到此已能通过 code 获取到用户的 openid 和 session_key, 但若不满足条件, 即使将小程序绑定到微信开放平台上, 也获取不到 unionid, 所以此方式不稳定, 推荐使用解密的方式获取数据.
方式二: 通过解密的方式获取用户 unionid
- /**
- * 通过 encryptedData,sessionKey,iv 获得解密信息, 拥有用户丰富的信息, 包含 openid,unionid, 昵称等
- */
- public static JSONObject decryptWxData(String encryptedData, String sessionKey, String iv) throws Exception {
- log.info("============ 小程序登录解析数据方法开始 ==========");
- String result = AesCbcUtil.decrypt(encryptedData, sessionKey, iv, "UTF-8");
- JSONObject userInfo = new JSONObject();
- if (null != result && result.length()> 0) {
- userInfo = JSONObject.parseObject(result);
- }
- log.info("result:" + userInfo);
- log.info("============ 小程序登录解析数据方法结束 ==========");
- return userInfo;
- }
- 补充 1: AesCbcUtil 工具类, 直接复制即可, 需要添加 bouncycastle 依赖. BouncyCastle 是一个开源的加解密解决方案, 官网可查看 http://www.bouncycastle.org/
- package com.luwei.common.utils;
- import org.bouncycastle.jce.provider.BouncyCastleProvider;
- import org.apache.commons.codec.binary.Base64;
- import javax.crypto.Cipher;
- import javax.crypto.spec.IvParameterSpec;
- import javax.crypto.spec.SecretKeySpec;
- import java.security.AlgorithmParameters;
- import java.security.Security;
- /**
- * Updated by huanglp
- * Date: 2018-11-28
- */
- public class AesCbcUtil {
- static {
- Security.addProvider(new BouncyCastleProvider());
- }
- /**
- * AES 解密
- *
- * @param data // 被加密的数据
- * @param key // 加密秘钥
- * @param iv // 偏移量
- * @param encoding // 解密后的结果需要进行的编码
- */
- public static String decrypt(String data, String key, String iv, String encoding) {
- // org.apache.commons.codec.binary.Base64
- byte[] dataByte = Base64.decodeBase64(data);
- byte[] keyByte = Base64.decodeBase64(key);
- byte[] ivByte = Base64.decodeBase64(iv);
- try {
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
- SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
- AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
- parameters.init(new IvParameterSpec(ivByte));
- cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
- byte[] resultByte = cipher.doFinal(dataByte);
- if (null != resultByte && resultByte.length> 0) {
- return new String(resultByte, encoding);
- }
- return null;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- }
到此已经获取到 JSONObject 类型的 userInfo, 包含 openid,unionid, 昵称, 头像等数据
后续可以将用户信息保存到数据库, 再返回给前端一个 token 即可, shiro 经过公司封装了一层, 代码如下:
- ..
- // 获得用户 ID
- int userId = wxUser.getWxUserId();
- shiroTokenService.afterLogout(userId);
- String uuid = UUID.randomUUID().toString();
- String token = StringUtils.deleteAny(uuid, "-") + Long.toString(System.currentTimeMillis(), Character.MAX_RADIX);
- shiroTokenService.afterLogin(userId, token, null);
- return token;
二, 以下为公众号 (网页) 授权的代码:
网页授权更加简单, 可查看 官方文档
需添加 riversoft 相关依赖包, 公众号网页授权, 只需要将公众号绑定了开放平台, 就能获取到 unionid 及其他用户信息.
- public static OpenUser webSiteLogin(String code, String state) {
- log.info("============ 微信公众号 (网页) 授权开始 ===========");
- WxProperties properties = WeiXinPropertiesUtils.getWxProperties();
- AppSetting appSetting = new AppSetting(properties.getAppId(), properties.getAppSecret());
- OpenOAuth2s openOAuth2s = OpenOAuth2s.with(appSetting);
- AccessToken accessToken = openOAuth2s.getAccessToken(code);
- // 获取用户信息
- OpenUser openUser = openOAuth2s.userInfo(accessToken.getAccessToken(), accessToken.getOpenId());
- log.info("============ 微信公众号 (网页) 授权结束 ===========");
- return openUser;
- // 后续, 可将用户信息保存
- // 最后一步, 生成 token 后, 需重定向回页面
- //return "redirect:" + state + "?token=" + token;
- }
以下就是本人整理的关于微信公众号授权和小程序授权的一些经验和问题汇总, 希望大家能够从中获得解决方法.
来源: https://juejin.im/post/5c125b5f6fb9a049b13e1404