One, the introduction
The most complete network of front and back end separation of wechat web page authorization solution. If there is a better optimization plan, welcome to exchange more, the author’s contact information at the end of the article, welcome to bother.
Second, the steps of web page authorization
- 1 Step 1: The user agrees to authorize and obtains the code
- Step 2: Exchange web access_token through code
- 3 Step 3: Refresh access_token (if necessary)
- 4 Step 4: Pull user information (scope is SNsapi_userinfo)
- 5 Appendix: Verify the validity of access_token
Please refer to the official documentation for details
Note: The access_token here belongs to the web authorization access_token, not the common authorization access_token. The official explanation is as follows:
1. Wechat web page authorization is realized through OAuth2.0 mechanism. After the user is authorized to the public account, the public account can obtain a unique interface call certificate (web page authorization access_token). The access_token can be used to invoke the interface after authorization, such as obtaining basic user information. 2. For other wechat interfaces, ordinary access_token calls need to be obtained through the “Access Access_token” interface in basic support.
But not very clearly. The difference between the two is:
- First, webpage authorization access_token can obtain user information as long as the user permits, can not pay attention to the public account, while ordinary access_token does not pay attention to the public account, access to user information is empty;
- Second, the daily limit of the two is different by the number of times. The ordinary access_token is called 2000 times a day, the access to web page authorization is unlimited times, and the access to user information is 50,000 times a day.
Third, back-end access
The open source tool Weixin-Java-tools is used at the back end
3.1 Pom.xml introduces jar packages
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>3.8.0</version>
</dependency>
Copy the code
3.2 Application. yml Add configuration
Here you can use your own AppID and AppSecret to apply for a test account
# wechat official account
wechat:
mpAppId: appid
mpAppSecret: appsecret
Copy the code
3.3 Creating and reading the wechatmpproperties.java configuration file
package com.hsc.power.dm.wechat.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/** * wechat public number profile **@author liupan
* @dateThe 2020-05-26 * /
@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WechatMpProperties {
private String mpAppId;
private String mpAppSecret;
}
Copy the code
3.4 Creating a custom wechat configuration wechatmpconfig.java
package com.hsc.power.dm.wechat.config;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/** * wechat public number configuration **@author liupan
* @dateThe 2020-05-26 * /
@Component
public class WechatMpConfig {
@Autowired
private WechatMpProperties wechatMpProperties;
/** * Information required to configure WxMpService **@return* /
@Bean // This annotation specifies that the method is executed and the objects returned by the method are managed by the Spring container when the Spring container starts
public WxMpService wxMpService(a) {
WxMpService wxMpService = new WxMpServiceImpl();
// Set the location where the configuration information is stored
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
return wxMpService;
}
/** * Configure appID and appsecret **@return* /
@Bean
public WxMpConfigStorage wxMpConfigStorage(a) {
// Using this implementation class means that configuration information is stored in memory
WxMpDefaultConfigImpl wxMpDefaultConfig = new WxMpDefaultConfigImpl();
wxMpDefaultConfig.setAppId(wechatMpProperties.getMpAppId());
wxMpDefaultConfig.setSecret(wechatMpProperties.getMpAppSecret());
returnwxMpDefaultConfig; }}Copy the code
3.5 Creating a wechat user Bean
package com.hsc.power.dm.wechat.vo;
import lombok.Data;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
@Data
public class WechatUser {
public WechatUser(WxMpUser wxMpUser, String accessToken) {
this.setAccessToken(accessToken);
this.setOpenid(wxMpUser.getOpenId());
this.setUnionId(wxMpUser.getUnionId());
this.setNickname(wxMpUser.getNickname());
this.setLanguage(wxMpUser.getLanguage());
this.setCountry(wxMpUser.getCountry());
this.setProvince(wxMpUser.getCity());
this.setCity(wxMpUser.getCity());
this.setSex(wxMpUser.getSex());
this.setSexDesc(wxMpUser.getSexDesc());
this.setHeadImgUrl(wxMpUser.getHeadImgUrl());
}
private String openid;
private String accessToken;
private String unionId;
private String nickname;
private String language;
private String country;
private String province;
private String city;
private Integer sex;
private String sexDesc;
private String headImgUrl;
}
Copy the code
3.6 Authorization Interface wechatController.java
-
- /auth: obtains the forward address of authorization
-
- /auth/user/info: obtains user information after the initial authorization
-
- /token/user/info: obtains user information through silent authorization
package com.hsc.power.dm.wechat.web;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.hsc.power.core.base.ret.Rb;
import com.hsc.power.dm.wechat.vo.WechatUser;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.net.URLEncoder;
/** * wechat public account interface **@author liupan
* @dateThe 2020-05-26 * /
@Slf4j
@RestController
@RequestMapping("/wechat")
public class WechatController {
@Autowired
private WxMpService wxMpService;
/** * get the code argument **@paramReturnUrl Indicates the URL to jump to@return* /
@GetMapping("/auth")
public Rb<String> authorize(@RequestParam String authCallbackUrl, @RequestParam String returnUrl) {
// Hardcode our callback address here for now for debugging
// Get the redirected URL returned by wechat
String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(authCallbackUrl, WxConsts.OAuth2Scope.SNSAPI_USERINFO, URLEncoder.encode(returnUrl));
log.info(Get code, redirectUrl = {}", redirectUrl);
return Rb.ok(redirectUrl);
}
/** * First authorization to obtain user information **@param code
* @param returnUrl
* @return* /
@GetMapping("/auth/user/info")
public Rb<WechatUser> userInfo(@RequestParam("code") String code, @RequestParam("state") String returnUrl) {
WxMpOAuth2AccessToken wxMpOAuth2AccessToken;
WxMpUser wxMpUser;
try {
// Use code to obtain access_token information
wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null);
} catch (WxErrorException e) {
log.error("[wechat webpage authorization] exception, {}", e);
throw ExceptionUtils.mpe(e.getError().getErrorMsg());
}
// Obtain the user's OpenID from access_token information
String openId = wxMpOAuth2AccessToken.getOpenId();
log.info(Obtain openId, openId = {}, openId);
WechatUser wechatUser = new WechatUser(wxMpUser, wxMpOAuth2AccessToken.getAccessToken());
return Rb.ok(wechatUser);
}
/** * Silent authorization obtains user information and determines whether accessToken is invalid. If accessToken is invalid, refresh accecssToken *@param openid
* @param token
* @return* /
@GetMapping("/token/user/info")
public Rb<WechatUser> getUserInfo(@RequestParam String openid, @RequestParam String token) {
WxMpOAuth2AccessToken wxMpOAuth2AccessToken = new WxMpOAuth2AccessToken();
wxMpOAuth2AccessToken.setOpenId(openid);
wxMpOAuth2AccessToken.setAccessToken(token);
boolean ret = wxMpService.oauth2validateAccessToken(wxMpOAuth2AccessToken);
if(! ret) {// It is invalid
try {
/ / refresh accessToken
wxMpOAuth2AccessToken = wxMpService.oauth2refreshAccessToken(wxMpOAuth2AccessToken.getRefreshToken());
} catch (WxErrorException e) {
log.error("[wechat webpage authorization] failed to refresh token, {}", e.getError().getErrorMsg());
throwExceptionUtils.mpe(e.getError().getErrorMsg()); }}// Get user information
try {
WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null);
WechatUser wechatUser = new WechatUser(wxMpUser, wxMpOAuth2AccessToken.getAccessToken());
return Rb.ok(wechatUser);
} catch (WxErrorException e) {
log.error("[wechat webpage authorization] failed to obtain user information, {}", e.getError().getErrorMsg());
throwExceptionUtils.mpe(e.getError().getErrorMsg()); }}}Copy the code
Four, front-end access
4.1 Route Interception
NoAuth Configures whether the authorization page is required
router.beforeEach((to, from, next) = > {
// Authorized by wechat public account
if(! to.meta.noAuth) {// Routing requires authorization
if (_.isEmpty(store.getters.wechatUserInfo)) {
// Get user information
if(! _.isEmpty(store.getters.openid) && ! _.isEmpty(store.getters.accessToken) ) {// OpenID and accessToken exist, already granted
// Determine whether accessToken is expired, refresh the token after expiration, and obtain user information
store.dispatch('getUserInfo')
next()
} else {
// Todo redirect page authorization
// Record the current page URL
localStorage.setItem('currentUrl', to.fullPath)
next({name: 'auth'}}})else {
// Todo already has user information that needs to be updated periodically
next()
}
} else {
// Routing does not require authorization
next()
}
})
Copy the code
4.2 Authorization Page
{
path: '/auth'.name: 'auth'.component: resolve= > {
require(['@/views/auth/index.vue'], resolve)
},
meta: {
noAuth: true}},Copy the code
<template></template> <script> import config from '@/config' import WechatService from '@/api/wechat' export default { Mounted () {WechatService. Auth (config. WechatAuthCallbackUrl). Then (res = > {the if (res) ok ()) {/ / access authorization page directly after the jump window.location.href = res.data } }) } } </script>Copy the code
4.3 authorized store
Authorization and user information are stored in VUEX
import _ from 'lodash'
import WechatService from '@/api/wechat'
import localStorageUtil from '@/utils/LocalStorageUtil'
export default {
state: {
unionId: ' '.openid: ' '.accessToken: ' '.wechatUserInfo: {}},getters: {
unionId: state= > {
return state.unionId || localStorageUtil.get('unionId')},openid: state= > {
return state.openid || localStorageUtil.get('openid')},accessToken: state= > {
return state.accessToken || localStorageUtil.get('accessToken')},wechatUserInfo: state= > {
return state.wechatUserInfo || localStorageUtil.get('wechatUserInfo')}},mutations: {
saveWechatUserInfo: (state, res) = > {
state.wechatUserInfo = res
// todo save to storage, set a certain date, periodic update
state.unionId = res.unionId
state.openid = res.openid
state.accessToken = res.accessToken
localStorageUtil.set('unionId', res.unionId)
localStorageUtil.set('openid', res.openid)
localStorageUtil.set('accessToken', res.accessToken)
// Save userInfo and set the validity period to 30 days by default
localStorageUtil.set('wechatUserInfo', res, 30)}},actions: {
// Silent authorization to obtain user information
async getUserInfo({ commit, getters }) {
const openid = getters.openid
const token = getters.accessToken
if(! _.isEmpty(openid) && ! _.isEmpty(token)) {// OpenID and accessToken exist, already granted
// Determine whether accessToken is expired, refresh the token after expiration, and obtain user information
const res = await WechatService.getUserInfo(openid, token)
if (res.ok()) {
// Todo determines whether res.data is incorrect
commit('saveWechatUserInfo', res.data)
}
}
},
// Obtain user information for the first time
async getAuthUserInfo({ commit }, { code, state }) {
if(! _.isEmpty(code) && ! _.isEmpty(state)) {const res = await WechatService.getAuthUserInfo(code, state)
if (res.ok()) {
commit('saveWechatUserInfo', res.data)
}
}
}
}
}
Copy the code
4.4 Customizing the Storage Tool LocalStorageutil.js
Localstorageutil. js: Used to set the preservation validity period
In this case, the user information is saved for 30 days. According to 4.1 Route Interception, the user information expires and authentication needs to be performed again. I feel this way is not good, but the monthly limit of obtaining user information is 50,000 times. I don’t want to call the interface to obtain user information every time. Is there a better solution?
import _ from 'lodash'
import moment from 'moment'
export default {
* @param {*} key * @param {*} defaultValue */
get(key, defaultValue) {
return this.parse(key, defaultValue)
},
Obj * @param {*} key * @param {*} obj * @param {Integer} Expires Time: days */
set(key, obj, expires) {
if (expires) {
const tmpTime = moment()
.add(expires, 'days')
.format('YYYY-MM-DD')
const handleObj = { expires: tmpTime, value: obj }
localStorage.setItem(key, JSON.stringify(handleObj))
} else {
if (_.isObject(obj)) {
localStorage.setItem(key, JSON.stringify(obj))
} else {
localStorage.setItem(key, obj)
}
}
},
/** * Remove key * @param {*} key */ from session-storage
remove(key) {
localStorage.removeItem(key)
},
/** * Retrieve the key from session-storage and objectify the value * @param {*} key * @param {*} defaultValue */
parse(key, defaultValue) {
let value = localStorage.getItem(key)
if (_.isObject(value)) {
const valueObj = JSON.parse(value)
if (valueObj.expires) {
// There is an expiration date: before the present time, the expiration date
if (moment(valueObj.expires).isBefore(moment(), 'day')) {
/ / delete
this.remove(key)
// Return directly
return null
}
return valueObj.value
}
// Return the object directly without expiration time
return valueObj
}
// Not an object, return value
return value || defaultValue
}
}
Copy the code
So far, in the wechat developer tools can obtain user information, effective pro test.
Open source is not easy, and use and cherish!
Spring Boot+Vue front and back end separation oF wechat public account webpage authorization solution