1.介绍下sso
这里有个优化策略,就是每次去系统A或者系统B,他们验证都需要去sso认证中心进行认证,浪费网络资源,如果能够生成一个系统A的tocken或者系统B的tocken则就不用每次去sso认证中心进行认证
2.介绍下jwt技术:
1.jwt技术生成一个加密的tocken,作为用户登陆的令牌,当用户登陆成功后,发送给客户端,请求需要登陆的资源或者接口,需要携带tocken,通过后端进行验证tocken是否合法
2.jwt有3部分组成:1.指定签名,固定好的{‘type’:‘JWT’,‘alg’:‘HS256’} 2.生成签发人,用户id 3.秘钥(自己设置)
3.wx扫码登陆+回调对应的地址
大致叙述整个过程
1.点击登陆,调用sso服务,获取微信登陆的二维码
2.sso服务根据微信提供的接口文档,生成二维码登陆,返回数据到前端
3.前端拿到链接,自动跳转微信登陆页面
4.手机授权扫码登陆
5.微信调用回调地址
6.回调到业务接口中:
a.如果用户存在则登陆,用户不存在注册
b.使用jwt技术生成tocken,同时存在cookie和redis中
c.需要记录用户的上次登陆信息,如果在其他机器登陆,则需要提掉线
数据库的设计
微信开放平台会关联web申请的账号,公众号,手机端这些,这三个账号是不同的,其中微信开放平台的账号的唯一的,对应数据库中微信unionid,微信的唯一标识以微信unionid为准
csrf跨站伪造攻击
wxMpService.buildQrConnectUrl这个方法有三个参数,一个是回调地址,一个是作用域,一个是csrfkey,其中这个csrf:跨站伪造攻击,回调链接中后面会有?state=csrfkey,需要验证csrfkey是不是我们存储的,如果是说明此链接是安全的不是伪造的
wx扫码扫码登陆的业务逻辑分析
1.获取一个csrfkey,然后在redis中存储
2.通过wxmpservice创建一个url地址,有3个参数,第一个是回调地址,第二个是作用域,第三个是csrf
回调的业务逻辑分析
前面有个回调地址参数验证
主要验证csrf是不是我们之前在redis当中存储的
doaction中的操作
1.通过code去获取access_tocken
2.通过code获取access_tocker,用access_tocken获取refresh_tocken,需要保存refresh_tocken在redis中,下次登陆的时候,如果refreshTocken存在则直接获取access_tocken,不需要用户重新授权
1.如果refreshtocken为null,则通过code获取access_tocken,通过access_tocken获取refresh_tocken,将refresh_tocken存储到redis中,过期时间设置为28天
2.如果refreshtocken不为null,则通过refreshtocken获取access_tocken
3.通过accesstocken获取微信的用户信息,(openid,unionid)其中openid就是微信用户的id,而unionid在web端,公众号端,手机端唯一。需要判断unionid在数据库user表中 是否存在 如果不存在,则注册;
4.使用jwt技术生产tocken,需要把tocken存储起来。提掉线功能(通过redisTemplate获得一个oldtocken,如果oldtocken!=null,则删除oldtocken)
5.返回给前端tocken,存在cookie中,下次请求的时候从cookie中获取tocken
在回调地址之后,是有一个测试的如下图
只要有tocken,说明就成功了,没有跳转页面,并且登陆那里没有变更微信头像,是因为下来要做一个用户的信息,登陆完成需要获取用户信息,才能完成前端页面的展示,需要拿上tocken来后端进行授权,上一步中,将tocken直接写到了cookie中,也就是从cookie拿到tocken来后端做验证,所以这里写了个登陆拦截,进行tocken认证,认证通过后
登陆拦截器
流程
1.实现登录拦截器,需要登录才能访问的接口,都会被拦截
2.要从cookie中拿到对应的token,通过request获取cookies数组,再遍历数组,找到对应的tocken
3.根据token去做对应的认证,认证通过,拿到userid。(这一步,分为两部分,一部分是检查tocken是否合法(JwtUtil.parseJWT),另一部分是判断redis中是否有tocken)通过try-catch一包,如果出现异常则拿不到了
4.通过ThreadLocal将userid放入其中,后序的接口都可以通过threadlocal方便拿到用户id
package com.mszlu.xt.sso.handler