項目基於微信公衆號開發,業務徹底依賴微信受權,也就是用戶進入頁面已經完成受權獲取到用戶的OpenId。前端
須要有一個受權中間頁:author.vuevue
路由index.jsjava
// 全局守衛,微信受權
router.beforeEach((to, from, next) => {
// 路由發生變化修改頁面title
if (to.meta.title) {
document.title = to.meta.title
}
if (process.env.NODE_ENV !== 'development') {
const token = window.localStorage.getItem('token')
if (token) {
if (to.path === '/author') {
next({
path: '/'
})
} else {
next()
}
} else {
if (to.path !== '/author') {
// 保存用戶進入的url
window.localStorage.setItem('authUrl', to.fullPath)
// 跳轉到微信受權頁面
window.location.href = process.env.BASE_URL + '/wx/OAuth2/index'
} else {
next()
}
}
} else {
window.localStorage.setItem('token', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJvUUFFYndSSU5VVlhPLVZoOWhEcDUzX3RNeEgwIn0.eShRG4fVFFv4w2gHnkyh7QDdVpG1meOHSZXOrbq-psE')
}
next()
})
Author.vueios
<template>
<div>受權中</div>
</template>
<script>
export default { name: 'Author', data () { return { user: null } }, created () { // url中獲取參數token const wxToken = this.$route.query.token // url中獲取參數code const code = this.$route.query.code // 後端重定向獲取參數,判斷是否處理成功 200:成功 if (wxToken && Number(code) === 200) { // 將token放入本地緩存 window.localStorage.setItem('token', wxToken) // 從本地緩存中獲取用戶第一次請求頁面URL const historyUrl = window.localStorage.getItem('authUrl') // 跳轉頁面 this.$router.push(historyUrl) } else { // 沒有拿到後臺訪問微信返回的token // 清空本地緩存 window.localStorage.removeItem('token') window.localStorage.removeItem('authUrl') } } } </script> <style scoped> </style>
/**
* 微信受權 --- OATH2 -- 第一種方式(推薦)
* 第一步:前端請求-/wx/oAth2/index
* 第二步:重定向-微信服務器
*/
@PassToken
@GetMapping(value = "/wx/OAuth2/index") public void OAth2(HttpServletResponse response) throws IOException{ response.sendRedirect(wxMpService.oauth2buildAuthorizationUrl(baseUrl + "/wx/OAuth2/redirect", WxConsts.OAuth2Scope.SNSAPI_USERINFO, null)); } /** * 微信受權 -- 微信回調 * 第一步:獲取code * 第二步:經過code獲取用戶信息 * 第三步:Jwt生成Token令牌 * 第四步:重定向 --> 前端頁面 */ @PassToken @GetMapping(value = "/wx/OAuth2/redirect") public void OAth2Return(HttpServletRequest request, HttpServletResponse response) throws IOException,WxErrorException{ String code = request.getParameter("code"); // 獲取用戶信息 WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpService.oauth2getAccessToken(code), null); log.info("[微信受權]--------拉取用戶信息詳細以下:{}",wxMpUser); //將微信用戶信息入庫 wxUserInfoService.insertWxUser(wxMpUser); //生成token令牌 String token = JWT.create().withAudience(wxMpUser.getOpenId()).sign(Algorithm.HMAC256(jwtSecret)); //重定向地址 String redirectUrl = frontUrl + "/#/author" + "?token=" + token + "&code=200"; response.sendRedirect(redirectUrl); }
前端獲取到token令牌以後,前端每次請求,後端如何獲取OpenId以及業務處理?json
request.jsaxios
// 請求攔截器
axios.interceptors.request.use(function (config) {
config.headers['Content-Type'] = 'application/json;charset=UTF-8'
// 判斷本地緩存是否存在token,若是存在的話,則每一個http header都加上token
if (window.localStorage.getItem('token')) {
config.headers.authorization = window.localStorage.getItem('token')
}
return config
}, function (error) {
return Promise.reject(error)
})
JwtInterceptor.java後端
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
// 從 http 請求頭中取出 token
String token = httpServletRequest.getHeader("Authorization"); // 若是不是映射到方法直接經過 if(!(object instanceof HandlerMethod)){ return true; } HandlerMethod handlerMethod=(HandlerMethod)object; Method method=handlerMethod.getMethod(); // OPTIONS請求類型直接返回不處理 if ("OPTIONS".equals(httpServletRequest.getMethod())){ return false; } //檢查是否有passToken註釋,有則跳過認證 if (method.isAnnotationPresent(PassToken.class)) { PassToken passToken = method.getAnnotation(PassToken.class); if (passToken.required()) { return true; } } //校驗token,而且將openId放入request中 if (StrUtil.isNotEmpty(token)){ // 驗證 token JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecret)).build(); try { jwtVerifier.verify(token); } catch (JWTVerificationException e) { logger.info("token校驗未經過"); httpServletResponse.getWriter().println(JSONUtil.toJsonStr(Result.need2BLogged())); return false; } // 獲取 token 中的 openId String openId; try { openId = JWT.decode(token).getAudience().get(0); httpServletRequest.setAttribute("openId",openId); } catch (JWTDecodeException j) { throw new RuntimeException("401"); } } //檢查有沒有須要用戶權限的註解 if (method.isAnnotationPresent(UserLoginToken.class)) { UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class); if (userLoginToken.required()) { // 執行認證 if (token == null) { throw new RuntimeException("無token,請從新登陸"); } // 獲取 token 中的 openId String openId; try { openId = JWT.decode(token).getAudience().get(0); } catch (JWTDecodeException j) { throw new RuntimeException("401"); } // 經過 openId 查詢用戶是否綁定手機號 if (objectRedisTemplate.hasKey(userIdKey + openId)) { logger.info("經過FRDIES用戶攔截器"); return true; } else { logger.info("REDIS:{Redis has no user information}"); //根據 openId 查詢該用戶的信息 BaseUserInfo userInfo = baseController.getUserInfo(httpServletRequest, httpServletResponse); if (userInfo != null && StrUtil.isNotEmpty(userInfo.getPhone())){ logger.info("經過用戶攔截器"); return true; }else{ // 未綁定手機用戶返回 httpServletResponse.getWriter().println(JSONUtil.toJsonStr(Result.need2BLogged())); return false; } } } } return true; }
@PassToken緩存
package com.yhzy.zytx.jwt.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @ClassName PassToken * @Description 自定義註解(跳過驗證Token) * @Author 天生傲骨、怎能屈服 * @Date 2019/5/22 13:38 * @Version 1.0 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface PassToken { boolean required() default true; }
到這裏整個先後分離微信受權的流程就完了,但願能夠幫助到你們!!!服務器