一、基本介绍
⭐️ H5 网页授权的核心:获取 openid(openid 是每个用户针对每个公众号都会产生的一个安全的,识别用户的,唯一标识用户的字符串)
⭐️ 为什么要获取用户的 openid 呢?用于唯一识别一个用户,为了让系统管理员知道是那个微信用户在系统中操作(如:是那个微信用户提交了表单)
二、获取 openid(前端代码)
(1) 创建一个 uniapp 项目
(2) 获取步骤
官方介绍:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
a. 介绍
code: 用户临时登录凭证
上面的链接的核心参数的介绍:
⭐️ ① appid: 应用的唯一标识
⭐️ ② redirect_uri: 获取到 code 后要访问的链接(通常是某个 Controller 接口)
⭐️ ③ scope: ❤️a. 如果值是 snsapi_base 表示是静默授权(系统悄悄获得用户的 openid,无需用户点击按钮)❤️ b. 如果值是 snsapi_userinfo 表示是非静默授权(系统弹框让用户点击【确定】按钮后方可获取用户的 openid)
b. 前端代码
在刚刚创建的 uniapp 项目的 App.vue 文件中的 onLoad(应用第一次被用户访问的时候会调用该方法) 中写如下代码
<script>
export default {
onLaunch: function() {
// 用户第一次进入程序的时候自动调用 login 方法
this.login()
},
methods: {
login() {
const appid = 'wxebbeb8968f1c9b29'
const redirectUri = encodeURIComponent(BASE_NETWORK_URI + 'authOpenIds/h5')
const scope = 'snsapi_userinfo'
let url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=".concat(appid)
.concat("&redirect_uri=").concat(redirectUri).concat("&response_type=code&scope=").concat(scope)
.concat("&state=STATE #wechat_redirect")
// 访问
window.location.href = url
},
},
}
</script>
<style>
</style>
c. 后端代码
用于获取 openid 的工具类
public class OpenidUtil{
public static final String APP_ID = "wxc436706a34c9dcd8";
public static final String APP_SECRET = "7fe003e0a896420042307f567c58144a";
// Spring 提供的发送 HTTP 请求的工具
private static RestTemplate restTemplate = new RestTemplate();
public static final String ACCESS_TOKEN = "access_token";
public static final String EXPIRES_IN = "expires_in";
public static final String OPEN_ID = "openid";
public static final String REFRESH_TOKEN = "refresh_token";
public static final String SCOPE = "scope";
private static AccessTokenExpiresIn accessTokenExpiresIn;
// 获取普通 access_token 的请求路径
private static String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=gqAPPID&secret=gqAPPSECRET";
private static String authorizeUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=gqAPPID&redirect_uri=gqREDIRECT_URI&response_type=code&scope=gqSCOPE&state=gqSTATE#wechat_redirect";
// 获取网页授权 access_token 的请求路径
private static String authorizeTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=gqAPPID&secret=gqSECRET&code=gqCODE&grant_type=authorization_code";
// 拉取用户信息的请求路径
private static String getUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=gqACCESS_TOKEN&openid=gqOPENID&lang=zh_CN";
/**
* 通过 code 值获取网页授权 access_token、openid等
*
* @param code 当用户点击同意授权后可获得
* @return 携带返回数据的 Map
*/
public static Map<String, String> getH5OpenIdAccessTokenByCode(String code) {
HashMap<String, String> resultMap = new HashMap<>();
String url = authorizeTokenUrl.replace("gqAPPID", APP_ID)
.replace("gqSECRET", APP_SECRET)
.replace("gqCODE", code);
String resultStr = restTemplate.getForObject(url, String.class);
JSONObject resultJSON = JSONUtil.parseObj(resultStr);
return createResultMap(resultJSON, ACCESS_TOKEN, EXPIRES_IN, REFRESH_TOKEN, OPEN_ID, SCOPE);
}
/**
* 根据 accessToken 和 openid 获取用户信息
*
* @param accessToken
* @param openid
*/
public static Map<String, String> getUserInfoByOpenIdAccessToken(String accessToken, String openid) {
String url = getUserInfoUrl.replace("gqACCESS_TOKEN", accessToken)
.replace("gqOPENID", openid);
String userInfoStr = restTemplate.getForObject(url, String.class);
JSONObject returnJSON = JSONUtil.parseObj(userInfoStr);
HashMap<String, String> returnMap = new HashMap<>();
return createResultMap(returnJSON, OPEN_ID, "sex", "language", "city", "province", "country", "headimgurl");
}
/**
* 【方法】获取进入授权页面的请求路径
*
* @param appid 应用的唯一标识
* @param redirectUri 用户同意授权后跳转的地址(如果用户同意授权, 会跳转到该地址, 并携带参数 code),【类似 controller 的地址】
* @param scope 授权作用域, 有两种取值: snsapi_base(静默授权, 只得 openid)、snsapi_userinfo(弹出授权页面)
* @return 进入授权页面的请求路径
*/
public static String getAuthorizeUrl(String appid, String redirectUri, String scope) {
return authorizeUrl.replace("gqAPPID", appid == null ? APP_ID : appid)
.replace("gqREDIRECT_URI", redirectUri)
.replace("gqSCOPE", scope)
.replace("gqSTATE", "zgq");
}
/**
* 【方法】获取 access_token 以及 expires_in
*/
private static void getAccessTokenExpiresIn() {
HashMap<String, String> retMap = new HashMap<>();
// 给请求 url 设值
String url = accessTokenUrl.replace("gqAPPID", APP_ID).replace("gqAPPSECRET", APP_SECRET);
// 向 url 发送网络请求, 获得返回值字符串
String resultStr = restTemplate.getForObject(url, String.class);
// 把 resultStr 转换为 JSON 格式
JSONObject resultJSON = JSONUtil.parseObj(resultStr);
String accessToken = resultJSON.get(ACCESS_TOKEN).toString(); // 凭证值
String expiresIn = resultJSON.get(EXPIRES_IN).toString(); // 凭证有效时间
accessTokenExpiresIn = new AccessTokenExpiresIn(accessToken, expiresIn);
}
/**
* 【方法】获取 access_token
*
* @return access_token 值
*/
public static String getAccessToken() {
if (accessTokenExpiresIn == null || accessTokenExpiresIn.alreadyExpired())
getAccessTokenExpiresIn();
return accessTokenExpiresIn.getAccessToken();
}
/**
* 组建返回值 Map
*
* @param params Map 的 key 和 value 的名称一样
*/
private static Map<String, String> createResultMap(JSONObject resultJSON, String... params) {
HashMap<String, String> resultMap = new HashMap<>();
for (String p : params) {
resultMap.put(p, resultJSON.get(p).toString());
}
return resultMap;
}
/**
* 凭证和凭证有效时间类
*/
@lombok.Data
private static class AccessTokenExpiresIn {
private String accessToken;
// 过期时间
private long expireTime;
public AccessTokenExpiresIn(String accessToken, String expiresIn) {
this.accessToken = accessToken;
this.expireTime = System.currentTimeMillis() + Integer.parseInt(expiresIn) * 1000;
}
public boolean alreadyExpired() {
return System.currentTimeMillis() > expireTime;
}
}
}
前端的 redirect_uri 就是填写的 authOpenIds/h5,从而获取到 openid
@RestController
@RequestMapping("/authOpenIds")
public class AuthOpenIdController {
/**
* 授权后, 持久化 openid (把它保存在数据库中)
* 同时也可把 openid 保存到 Session 中
* @param request 使用 request 把 openid 缓存到服务器 session 中
*/
@ApiOperation("公众号 h5 授权")
@GetMapping("/h5")
public String h5(String code, String state, HttpServletRequest request) {
String h5OpenId = OpenIdUtil.getH5OpenId(code);
request.getSession().setAttribute("openid", h5OpenId);
System.out.println("h5OpenId: " + h5OpenId);
return "成功获得";
}
}