项目基于微信公众号开发,业务完全依赖微信授权,也就是用户进入页面已经完成授权获取到用户的OpenId。

需要有一个授权中间页:author.vue

基本实现思路:

  • 无论使用哪个url进入页面都会先触发router.beforeEach钩子。
  • 在router.beforeEach钩子函数中判断用户是否授权。
  • 若未授权则保存用户进入的url并请求后台接口获取微信授权(window.location.href=‘后台接口’)。
  • 后台调用微信接口授权获取用户信息及openId,将openId使用JWT生成一个唯一的token令牌,并将token已参数的形式拼接到url后面,然后重定向到前端author.vue页面。
  • author页面获取url中的token参数,将token参数保存到本地缓存。
  • 获取签名用户保存的url并跳转。

前端代码实现:

路由index.js

// 全局守卫,微信授权
router.beforeEach((to, from, next) => {// 路由发生变化修改页面titleif (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') {// 保存用户进入的urlwindow.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.vue

<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以及业务处理?

基本实现思路:

  • 前端使用axios请求拦截器,判断本地缓存是否存在token,如果存在的话,则为每个Http请求赋值token。
  • 后端使用拦截器拦截有@PassToken注解以外的方法,获取token值。如果token为null,直接返回错误码以及错误信息。
  • 验证token值是否有效,如有效,则解析openId,并将openId放入request中放行。如无效,直接返回错误码以及错误信息。
  • 拦截器放行,后端可直接通过request.getAttribute("openId")获取。

前端代码实现:

request.js

// 请求拦截器
axios.interceptors.request.use(function (config) {config.headers['Content-Type'] = 'application/json;charset=UTF-8'// 判断本地缓存是否存在token,如果存在的话,则每个http header都加上tokenif (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 请求头中取出 tokenString 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; }

到这里整个前后分离微信授权的流程就完了,希望可以帮助到大家!!!

转载于:https://www.cnblogs.com/itmrzhang/p/10997006.html

SPA单页应用前后分离微信授权相关推荐

  1. SPA单页应用的优缺点

    SPA单页应用的优缺点 Single Page Web Application是一种特殊的Web应用,其所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTML.JavaScrip ...

  2. java 单页面spa_Javascript 与 SPA单页Web富应用

    书单推荐 # <单页Web应用:JavaScript从前端到后端>http://download.csdn.net/detail/epubitbook/8720475 # <MVC的 ...

  3. SPA 单页Web应用

    定义 单页 Web 应用 (single-page application 简称为 SPA) 是一种特殊的 Web 应用.它将所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTM ...

  4. EASY spa单页面版文档

    1.1.导入项目 下载项目后进行解压 使用IDEA.WebStorm.HBuilder等前端开发工具打开 打开index.html点击右上角浏览器图标运行: 注意: 必须以http://的形式访问,而 ...

  5. spa:单页web应用(介绍,实现思路及技术点,路由,router-link相关属性)

    SPA是什么 单页Web应用(single page application,SPA),就是只有一个Web页面的应用, 是加载单个HTML页面,并在用户与应用程序交互时动态更新该页面的Web应用程序 ...

  6. Vue2单页应用导致的微信支付安全域名问题

    最近vue的单页应用在测试时遇到一个比较严重的问题,在微信内调起微信支付老是报错,排查后发现是安全域名的设置有问题.vue的单页应用有两种模式,hash和history两种,而hash模式下的url是 ...

  7. spa单页应用PHP,前端学习之路之SPA(单页应用)设计原理

    SPA设计 1.设计意义 前后端分离 减轻服务器压力 增强用户体验 Prerender预渲染优化SEO 前后端分离:前端做业务逻辑,后端处理数据和接口,耦合度减少,开发效率提高. 减轻服务器压力:一个 ...

  8. 前端学习之路之SPA(单页应用)设计原理

    SPA设计 1.设计意义 前后端分离 减轻服务器压力 增强用户体验 Prerender预渲染优化SEO 前后端分离:前端做业务逻辑,后端处理数据和接口,耦合度减少,开发效率提高. 减轻服务器压力:一个 ...

  9. 大熊君学习html5系列之------History API(SPA单页应用的必备------重构完结版)

    一,开篇分析 Hi,大家好!大熊君又和大家见面了,(*^__^*) 嘻嘻--,这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例, 让大家一步一步的体会" ...

最新文章

  1. DeepMind出品:终于不瞎编了!AI学会了“谷歌一下”,回答问题正确率达90%。
  2. 2020 Kaggle年度报告发布:美国公司最容易「人均百万」,90%数据科学家坚持终生学习...
  3. Android学习笔记--处理UI事件
  4. 【Android实战】记录自学自己定义GifView过程,能同一时候支持gif和其它图片!【有用篇】...
  5. FastJSON应用前测试--转载
  6. android 开发中java.lang.verifyerror问题
  7. vue父组件向子组件传递多个数据
  8. 实用技巧:使用 Google Analytics 跟踪 JS 错误
  9. node.js模块和包
  10. 如何用CSS画一个三角形
  11. 2017.4.16 级数求和 思考记录
  12. MPLS_×××入门中文版
  13. editGrid自定义列自定义F7
  14. 【云原生】设备云之前端可视化编程
  15. JS代码计算正方教务系统目前成绩的加权平均分和平均绩点
  16. MySQL基础~多表查询分类与SQL92和99语法如何实现内连接和外连接
  17. 想转行it , 培训还是自学?
  18. 论文笔记 Weakly Supervised Deep Detection Networks - CVPR 2016
  19. shutter截图编辑功能
  20. vue+elementui网上线上教学平台python+java

热门文章

  1. 分分钟入门【Nodejs】—一个网页是如何生成的
  2. $.messager.progress ajax,ajax异步上传文件返回undefined
  3. java 随机数生成实现_Java中生成随机数的实现方法总结
  4. linux内核编译 menuconfig详解,Linux内核编译menuconfig介绍
  5. 网站如何布局才能更利于提升用户体验?
  6. 网站建设特定操作流程了解一下不吃亏
  7. 北大计算机考研考什么时候,北大计算机考研复习计划有哪些
  8. JVM 调优 —— 新生代 Survivor 空间不足
  9. Dagger依赖注入注解的具体作用
  10. 搭建Android上的服务器 “实现隔空取物”