失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 微信小程序登录 + redis + 基于token的身份验证

微信小程序登录 + redis + 基于token的身份验证

时间:2022-09-26 09:57:50

相关推荐

微信小程序登录 + redis + 基于token的身份验证

官方时序图如下:

https://developers./miniprogram/dev/framework/open-ability/login.html

图里其实说的很清楚了,清理下流程:

1.前端调用wx.login()获取code值

2.前端通过调用wx.getUserInfo获取iv、rawData、signature、encryptedData等加密数据,传递给后端

3.服务器通过code请求api换回session_key和openid

4.服务器通过前端给的rawData 加获取的session_key使用sha1加密,计算出signature1

5.比对前端传的signature和自己算出来的signature1是否一致(防止数据不一致)

6.用AES算法解密encryptedData里的敏感数据

7.拿着敏感数据后做自己的逻辑

8.通知前端登陆成功

** 这里只是想拿到用户的openid,则直接1,3就可以做到了。如下:

第一步:

通过wx.login(微信前端--小程序)接口获取code,将code传到后台

注意:

code的来源:是用户打开小程序的时候,随机生成的,是腾讯生成的,每个code只能使用一次,因此,理论上这个code是安全的

package cn.wmyskxz.springboot.model.user;/*** @Author: Yangke* @Date: /3/31 15:52**/public class WeChatLoginModel {String code;public String getCode() {return code;}public void setCode(String code) {this.code = code;}}

第二步:

后台通过code访问微信(腾讯)接口,微信(腾讯)接口返回当前登录的信息:session_key及openid

返回的openid是每个用户唯一的,通过这个可以匹配 微信(腾讯)的用户跟我们的用户,就是我们后台通过openid来判断这个人是谁,

UserController.java 微信小程序登录

/*** 微信小程序登录** 登录成功后,将用户身份信息及session_key存入token* @param model* @return*/@ResponseBody@PostMapping("/weChatLogin")public SingleResult<String> weChatLogin(@RequestBody WeChatLoginModel model){/*** 登录日志:* id\ userid\ date\ wx_code\ createTime* create table loginLog (id varchar(50) primary key,userId varchar(50),logindate date,wxcode varchar(100),createtime datetime);*/SingleResult<String> result = new SingleResult<String>();//第三步:调用service.weChatLogin(model):后台检查openid是否存在,返回openid对应的用户WeChatLoginResult<UserAccount> loginResult = service.weChatLogin(model);//第四步:UserAccount user = loginResult.getUser();if(user == null ){result.setCode(0);result.setMessage("登录失败");}else {User u = new User();u.setId(user.getId());u.setPassword(user.getPassword() == null ? user.getWxopenid() : user.getPassword());u.setSessionKey(loginResult.getSession_key());String token = getToken(u);result.setToken(token);result.setCode(1);result.setMessage("登陆成功");}return result;}

其中:就是下面的第三步

//调用service.weChatLogin(model)

WeChatLoginResult<UserAccount> loginResult =service.weChatLogin(model);

第三步:

后台检查openid是否存在,

去UserService.java

@Overridepublic WeChatLoginResult<UserAccount> weChatLogin(WeChatLoginModel model){WeChatLoginResult<UserAccount> result = null;try {// code -> openidString urlFormat = "https://api./sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";String url = String.format(urlFormat, WeChat.appId, WeChat.secret, model.getCode());String json = WeChat.sendGet(url);//将json字符串转化成对象result = JSON.parseObject(json, WeChatLoginResult.class);if(result.getErrcode() == null){// 去数据库 检查 openId 是否存在 不存在就新建用户UserAccount user = userAccount.wechatOpenIdIsExists(result.getOpenid());if(user == null || user.getId() == null){// 不存在,就是第一次登录:新建用户信息user = new UserAccount();user.setId(UUID.randomUUID().toString());user.setWxopenid(result.getOpenid());user.setLasttime(new Date());userAccount.insert(user);}else {//如果存在,就不是第一次登录,更新最后登录时间user.setLasttime(new Date());userAccount.updateByPrimaryKeySelective(user);}result.setUser(user);// 保存登录日志LoginLog log = new LoginLog();log.setId(UUID.randomUUID().toString());log.setCreatetime(new Date());log.setLogindate(new Date());log.setUserid(user.getId());log.setWxcode(model.getCode());loginLog.insert(log);}else {System.out.println(json);}}catch (Exception e){System.out.println(e.getMessage());}return result;}

去数据库中检查openid是否存在:

UserAccountMapper.java

@Select("select * from useraccount where wxOpenId = #{wxOpenId}")UserAccount wechatOpenIdIsExists(String wxOpenId);

(1)如果不存在:就是该用户的第一次登录,后台数据库新添加一个用户信息

如果存在:就不是该用户的第一次登录,以前登陆过,就更新后台数据库中该用户的第一次登录时间

(2) 返回用户信息

第四步:

下发token

//第四步:UserAccount user = loginResult.getUser();if(user == null ){result.setCode(0);result.setMessage("登录失败");}else {User u = new User();u.setId(user.getId());//用户如果是第一次登录,那就是没有密码的,这里用openid当做密码u.setPassword(user.getPassword() == null ? user.getWxopenid() : user.getPassword());u.setSessionKey(loginResult.getSession_key());//利用User.class中的信息生成tokenString token = getToken(u);//下发tokenresult.setToken(token);result.setCode(1);result.setMessage("登陆成功");}return result;}

其中生成token的步骤:BaseController.java

利用JWT框架生成token

package cn.wmyskxz.springboot.controllers;import cn.wmyskxz.springboot.model.User;import com.auth0.jwt.JWT;import com.auth0.jwt.algorithms.Algorithm;import java.util.Date;/*** @Author: Yangke* @Date: /3/28 21:12**/public abstract class BaseController {protected String getToken(User user) {String token="";token= JWT.create().withKeyId(user.getId()).withIssuer("").withIssuedAt(new Date()).withJWTId("").withClaim("session_key", user.getSessionKey()).withAudience(user.getId()).sign(Algorithm.HMAC256(user.getPassword()));return token;}}

至此,再理一下上面的步骤:

(1)微信小程序通过访问wx.login获得一个code,返回给后台

(2)后台拿着这个code,调用腾讯的接口,获取到openid、seesion-key等信息,openid是用户唯一的

(3)后台拿着openid去数据库中检查,该用户是否是第一次登陆

如果是第一次登陆,那么就新建一个用户--UserAcount;如果不是第一次登陆,就修改该用户的最后登录时间

不管是不是第一次登录,都有了一个用户

(4)然后根据用户的信息利用JWT生成token,下发给微信小程序

(5)redis:服务器端把token存在redis里面:<key-userid; value-权限>

参考p2p项目

第五步

微信小程序收到token后,存起来

第六步

微信小程序请求后台

微信小程序把token放在请求头中

第七步

先介绍一个注解:

Authorize

说明:如果有这个注解,就需要验证token

package cn.wmyskxz.springboot.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/*** @Author: Yangke* @Date: /3/28 19:57*authorize 是判断 是否需要 token**/@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface Authorize {boolean required() default true;}

用拦截器,验证token

package cn.wmyskxz.springboot.interceptors;import cn.wmyskxz.springboot.annotation.AllowAnonymous;import cn.wmyskxz.springboot.annotation.Authorize;import cn.wmyskxz.springboot.model.User;import cn.wmyskxz.springboot.service.UserService;import com.auth0.jwt.JWT;import com.auth0.jwt.JWTVerifier;import com.auth0.jwt.algorithms.Algorithm;import com.auth0.jwt.exceptions.JWTDecodeException;import com.auth0.jwt.exceptions.JWTVerificationException;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.lang.reflect.Method;/*** @Author: Yangke* @Date: /3/28 20:00** 获取token并验证token**/public class AuthorizationInterceptor implements HandlerInterceptor {@AutowiredUserService userService;//拦截器:请求之前preHandle@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {// 如果不是映射到方法直接通过if (!(object instanceof HandlerMethod)) {return true;}HandlerMethod handlerMethod = (HandlerMethod) object;Method method = handlerMethod.getMethod();//检查是否有passtoken注释,有则跳过认证,注意:其中这个注解多余了if (method.isAnnotationPresent(AllowAnonymous.class)) {AllowAnonymous passToken = method.getAnnotation(AllowAnonymous.class);if (passToken.required()) {return true;}}//检查有没有需要用户权限的注解//如果有注解Authorize,就需要验证tokenif (method.isAnnotationPresent(Authorize.class)) {Authorize userLoginToken = method.getAnnotation(Authorize.class);if (userLoginToken.required()) {String token = httpServletRequest.getHeader("authorization");// 从 http 请求头中取出 token// 执行认证if (token == null) {throw new RuntimeException("无token,请重新登录");}// 获取 token 中的 user idString userId;try {// 获取 useriduserId = JWT.decode(token).getKeyId();// 添加request参数,用于传递useridhttpServletRequest.setAttribute("currentUser", userId);// 根据userId 查询用户信息User user = userService.getUserById(userId);if (user == null) {throw new RuntimeException("用户不存在,请重新登录");}try {String session_key = JWT.decode(token).getClaim("session_key").as(String.class);// 添加request参数,用于传递useridhttpServletRequest.setAttribute("sessionKey", session_key);}catch (Exception e){}// 验证 密码JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();try {jwtVerifier.verify(token);} catch (JWTVerificationException e) {throw new RuntimeException("401");}} catch (JWTDecodeException j) {throw new RuntimeException("401");}return true;}}return true;}@Overridepublic void postHandle(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,Object o, ModelAndView modelAndView) throws Exception {}//拦截器:请求之后:afterCompletion@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,Object o, Exception e) throws Exception {}}

流程:

1、从http请求头中取出token

String token = httpServletRequest.getHeader("authorization");

2、如果没有token,抛出异常,请用户登录

如果有token,利用JWT从token中取出userid,添加到request参数

3、根据userid去后台数据库中查询用户是否存在,如果不存在,抛出异常:用户不存在,请重新登录

User user = userService.getUserById(userId);

这个方法:

@Overridepublic User getUserById(String id) {UserAccount u = userAccount.selectByPrimaryKey(id);User user = new User();user.setId(u.getId());user.setPassword(u.getPassword() == null ? u.getWxopenid() : u.getPassword());user.setUsername(u.getUsername());return user;}

4、如果用户存在,再利用JWT从token中取出seesion-key,添加到request参数

String session_key = JWT.decode(token).getClaim("session_key").as(String.class);

5、验证密码:因为我生成token的时候,是存了密码的,这个就是检查一下密码对不对

验证 token里面的密码 跟 你存的 是不是一样

JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();try {jwtVerifier.verify(token);} catch (JWTVerificationException e) {throw new RuntimeException("401");}

6、最终token验证成功,返回true,放行

拦截器介绍一下:

preHandle:在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理;postHandle:在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView (这个博主就基本不怎么用了);afterCompletion:在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);

七-2

如果服务器端不存token,也不去访问数据库,那么怎么做?

JWT自带的验证方法:里面可以验证使用的算法是否一致 等

也可以是能成功解密 并且没有失效 并且当前访问的openid与token里面记录的openid是一致的,就放行

七-3

首先为什么用redis在服务器端存下来?

因为纯token的功能有限,不足以支持我们的需求,比如权限验证、登录下限制。换句话说,就是如果完全依赖jwt保存用户身份的话,那么只需要用户提供的token合法就是有效的,如果中间账号封禁了或者什么的仍然可以登录。所以我们加入了redis,发给用户的token只是记录身份信息,然后通过redis匹配,这样如果账号封禁或者权限修改了,那么我们只要在服务端redis里面改了,用户访问的时候去匹配redis就能实现正常的功能了。

服务器端利用redis,

(1)从http请求头中取出token

(2)如果没有token,抛出异常,请用户登录

如果有token,利用JWT从token中取出userid,添加到request参数

(3)根据userid去redis缓存里面查,如果有,并且权限允许;如果权限不允许,就抛出异常:权限不一致

(4)如果没有,那就再去数据库中查询

(5)根据userid去后台数据库中查询用户是否存在,如果不存在,抛出异常:用户不存在,请重新登录

(6)如果用户存在,再利用JWT从token中取出seesion-key,添加到request参数

(7)自带的再验证一下:jwtVerifier.verify(token);

(8)最终token验证成功,返回true,放行

第八步:

request里面有userid,后台就可以识别是对哪个用户做处理

参考:

/qq_38011415/article/details/82823778

/zeng1994/p/03303c805731afc9aa9c60dbbd32a323.html

如果觉得《微信小程序登录 + redis + 基于token的身份验证》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。