官方时序图如下:

https://developers.weixin.qq.com/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: 2019/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

@Override
    public WeChatLoginResult<UserAccount> weChatLogin(WeChatLoginModel model){
        WeChatLoginResult<UserAccount> result = null;
        try {
 
            // code  -> openid
            String urlFormat = "https://api.weixin.qq.com/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中的信息生成token
            String token = getToken(u);
            //下发token
            result.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: 2019/3/28 21:12
 **/
public abstract class BaseController {
 
    protected String getToken(User user) {
        String token="";
        token= JWT.create()
                .withKeyId(user.getId())
                .withIssuer("www.ikertimes.com")
                .withIssuedAt(new Date())
                .withJWTId("jwt.ikertimes.com")
                .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,下发给微信小程序

第五步
微信小程序收到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: 2019/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: 2019/3/28 20:00
 *
 * 获取token并验证token
 **/
public class AuthorizationInterceptor implements HandlerInterceptor {
    @Autowired
    UserService userService;
 
    //拦截器:请求之前preHandle
    @Override
    public 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,就需要验证token
        if (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 id
                String userId;
                try {
                    // 获取 userid
                    userId = JWT.decode(token).getKeyId();
                    // 添加request参数,用于传递userid
                    httpServletRequest.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参数,用于传递userid
                        httpServletRequest.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;
    }
 
 
    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception {
 
    }
 
    //拦截器:请求之后:afterCompletion
    @Override
    public 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);
这个方法:

@Override
public 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完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);
 
第八步:
request里面有userid,后台就可以识别是对哪个用户做处理
————————————————
版权声明:本文为CSDN博主「每天开心成为别人的望尘莫及」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39474604/article/details/100016352

微信小程序登录 + 基于token的身份验证相关推荐

  1. 微信小程序登录获取Token以及微信支付

    微信小程序登录获取Token 在调用登录接口api之前,要先获取以下五个参数 后四个参数获取:通过为button按钮添加 open-type="getUserInfo" (固定写法 ...

  2. 用户数据表设计借鉴 浅谈数据库用户表结构设计,第三方登录 基于 Token 的身份验证

    最近对用户数据表的设计比较感兴趣,看到了两篇比较好的文章. 浅谈数据库用户表结构设计,第三方登录 转载于: https://www.cnblogs.com/jiqing9006/p/5937733.h ...

  3. 微信小程token_微信小程序登录换取token

    前言: 这次主要是介绍些业务逻辑,技术点倒是没有多少.不过在开发中,优秀的编程思路同样是非常值得学习的. 最近小程序可以说在开发届狠狠的火了一把.微信小程序可以开发游戏,腾讯率先带头,做出了一个跳一跳 ...

  4. 手动生成token_微信小程序登录换取token的教程

    前言: 这次主要是介绍些业务逻辑,技术点倒是没有多少.不过在开发中,优秀的编程思路同样是非常值得学习的. 最近小程序可以说在开发届狠狠的火了一把.微信小程序可以开发游戏,腾讯率先带头,做出了一个跳一跳 ...

  5. 微信小程序python token验证_微信小程序登录对接Django后端实现JWT方式验证登录

    点击授权按钮后可以显示部分资料和头像,点击修改资料可以修改部分资料. 1.使用微信小程序登录和获取用户信息Api接口 2.把Api获取的用户资料和code发送给django后端 3.通过微信接口把co ...

  6. 【微信小程序】基于SpringBoot开发后端

    微信小程序登录流程: 主要步骤: 1.小程序端调用 wx.login()向微信接口服务获取临时登录凭证code,并上传至开发者服务端. 2.开发者服务端向微信服务接口服务调用 auth.code2Se ...

  7. 微信小程序登录功能的前端设计与实现

    导语 | 登录/注册这模块就像个冰山,我们通常以为它就是「输入账号密码,就完成登录了」,但实际下面还有各种需要考虑的问题.作为应用的基础能力,登录/注册的设计需要有足够的健壮性,避免出现全站性阻塞.同 ...

  8. java后端微信小程序登录与注册

    java后端微信小程序登录与注册&微信登录授权 分析: 微信小程序用户表 的字段来源于微信服务器 , 必须想办法去获取到对应的用户信息 找到微信开放平台: 微信开放平台 以下是微信开放平台给出 ...

  9. RuoYi-Vue微信小程序登录授权

    目前的框架中token是使用jwt生成,存储到redis控制token时效,而认证是使用UsernamePasswordAuthenticationToken实现的 微信小程序登录授权 需求分析 解决 ...

最新文章

  1. Cell颠覆性发现:中脑神经元的新作用
  2. android 设置布局横屏竖屏
  3. Nginx映射本地静态资源时,浏览器提示跨域问题解决
  4. 惠普照片打印软件_被看错的打印机?原来打印机还可以这么玩
  5. [html] 进入编辑页面时,如何把光标聚焦到第一个input?
  6. r语言 fread函数参数_R语言 第4章 初级绘图(6)
  7. mysql_real_connect阻塞_mysql_real_connect崩溃、未经处理的异常
  8. 【python基础知识】如何查看pycharm中已经安装的插件
  9. 如何做研究-精辟分析
  10. php小插件,PHPStrom中一些我收藏的小插件
  11. 有了这6款浏览器插件,浏览器居然“活了”?!媳妇儿直呼“大开眼界”
  12. TongWeb8知识总结
  13. 红巨星粒子插件Trapcode Suite 14.0 Win版全套中文完美汉化版
  14. 【NOI2011】 阿狸的打字机(AC自动机+树状数组)
  15. “此电话号码无法用于进行验证”的解决方法
  16. linux mlocate.db文件文件过大
  17. 小学生python编程写游戏_教你如何像玩游戏一样学Python
  18. Xmanager5 安装时出现安装程序集组件XXX时出错
  19. 使用驱动器H:中的光盘之前需要将其格式化
  20. 图像增强(拉普拉斯锐化增强)

热门文章

  1. 读《物理学的困惑》的思考
  2. 【论文笔记】《Social Influence-Based Group Representation Learning for Group Recommendation》
  3. 2016年十大顶级开源项目
  4. 分享自己觉得有意思的
  5. 绝地求生服务器排名网站,绝地求生大逃杀采用超性能服务器 支持所有宽带无延迟...
  6. andorid简单计算器java源码_求大佬用java写个简单计算器的代码
  7. 校级电子签章平台怎么建?“北邮样本”给出答案
  8. 短线实盘赛新第20期(总第61期)
  9. Python+ChatGPT实战之进行游戏运营数据分析
  10. 5V升压16.8V充电芯片