1.微信登录
微信登录属于第三方登录,和普通的账号密码登陆的最大的优点为方便快捷,且用户的基数比较大,
2.基于OAuth2.0下的微信登录
OAuth2.0是在客户端和服务端之间有一个授权层,用来将用户和客户端进行分开客户端登录授权层使用的是令牌,意思是给了多少的权限也就只能有多少的权限之类的资源
OAuth的四种模式
A-授权码模式:
你要去学校,学校有保安把大门守着,你先去找你的班主任说你要进学校,你班主任给你一个code,你把这个code去保安看,保安知道了,给你大门的钥匙(token)你拿到这个token就进入了学校
B-简化模式
就是你直接去找保安,他直接就把token给你,你拿到这个token就去开门去进入
一般是用于第三方单页面应用
C-密码模式
给你账号和密码你去开门,
这种是用于第一方的单页面应用以及第一方的原生app (内部使用)
D-客户端模式
你是个人就直接进入学校
所有最安全的模式是授权码模式
微信登录用的也是授权码模式
3.准备工作--以开发网站为案列
你要先打开微信开发平台,
进行相关的注册和申请的步骤
创建网站
获取到该网站的APPID 和APPSECRET 回调域(如果在公司,这里是直接找公司拿即可)
大致流程图如下
4.实现步骤
A-获取code
前端进行调用获取到code给后端进行传入
public AjaxResult wechat(Map<String, String> map) {
// 1.接收code,做校验
String code = map.get("code");
B-根据code进行发送http请求获取到token和appid
这是请求的链接,对应的填入要的数据即可
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
我这里是把这个请求的链接进行封装为一个枚举,然后在进行数据的替换
// 2.根据code从微信去获取token + openId
String getToken = RegisterUtils.getToken
.replace("APPID", RegisterUtils.APPID)
.replace("SECRET", RegisterUtils.SECRET)
.replace("CODE", code);
C-发送数据进行获取到返回的数据
这里返回的数据我进行了一个对象来进行接受,在进行json数据转实体对象的转化
/*发送Http获取到的是Stirng类型*/
String httpGet = HttpClientUtils.httpGet(getToken);
/*把String类型用JSON对象来接受一波*/
JSONObject jsonObject = JSONObject.parseObject(httpGet);
/*把里面的openid 和token拿出来*/
String access_token = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
D-将数据进行返回给前端即可,进行登录
5.其他步骤
如果你感觉微信登录就这样结束,那就错了,上面只是实现了第一次登录,问题来了
如果第二次,第n次登录的时候怎么办,这么实现免密登录?
5.1设计数据库
要知道是否是第一次还是第二次,那么就要在你的数据库留下东西来证明
这里我把登录成功之后会返回一些东西进行作为字段放在数据库
5.2验证是否存在---(存在)
当微信登录成功返回数据
拿出里面的数据openid去本地数据去查询有无这个包含该字段的对象
----(这里是存在就进行免密登录)
String access_token = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
// 3.根据openId查询本地的t_wxuser,看是否曾今已经扫码过
Wxuser wxuser = wxuserMapper.loadByOpenId(openid);
// 3.1. 有值且t_wxuser表中的user_id有值 曾今已经扫过,本次是第二...N扫码
if (wxuser != null && wxuser.getUserId() != null) {
/*以下是免密登录*/
Logininfo logininfoTmp = logininfoMapper.loadByUserId(wxuser.getUserId());
5.3创建自己程序端的用户
其实你的程序登录成功进入你的程序里,表面是登录微信成功就进入了,实际还有一步当登录成功就创建自己程序端的用户,(本意就是使用你的程序端的用户进入程序)
自己程序端用户:意思是通过比如手机号注册,短信注册,账号密码注册等;而不是第三方的用户
这大致是我程序的用户数据表,这里是只有你微信登录成功,我拿到你微信放回的资料。先进行创建一个装微信对象的表里面去,在对于的创建一个本地程序用户
5.4验证是否存在--(不存在)
当不存在,拿就要就要进行创建本地用户,和创建微信用户
这里需要给正在登录的用户跳转到本地用户注册的页面,拿就要告诉前端验证不存在进行跳转,所有这里我直接把验证失败消息放回,并且把openid和token一起带回
// 3.2. 没有,需要走微信绑定流程
HashMap<String, String> map2 = new HashMap<>();
map2.put("accessToken", access_token);
map2.put("openid", openid);
// 需要返回:token + openId
return new AjaxResult().setSuccess(false).setResultObj(map2);
这里是通过Ajax后置拦截器(后面有时间写4大拦截器)跳转到注册页面(在这里简称callback页面)
在callback页面用户进行手机号注册,
大致页面就是获取用户的手机号
说一下为什么这里明明说是微信登录,又在手机号登录
因为他是第一次登录,所有我要给他进行新建一个本地用户对象,我的本地用户对象里面的字段有个手机号(为了后面的业务)所有我这里可以先获取到他的手机号
当点击注册按钮之后的操作
前端携带上openid和token手机号,到后端
这里大致说一下步骤
A-先把前端给的数据进行基本的校验
B-拿到手机去本地用户表查询有无对象
C-又本地对象,我就直接创建微信用户表的对象即可
D-没有就根据这个手机号去创建本地对象在在对应的创建出微信对象
E-进行免密登录,把相关的信息进行封装为map放回前端即可
以下是相关的代码
public Map<String, Object> binderWechat(BinderDto dto) {
/*先进行基本的校验*/
if (StringUtils.isEmpty(dto.getPhone()) ||
StringUtils.isEmpty(dto.getVerifyCode()) ||
StringUtils.isEmpty(dto.getAccessToken()) ||
StringUtils.isEmpty(dto.getOpenId())) {
throw new MyException("参数不能为空");
}
/*校验手机验证码是否输入成功*/
String keyTmp = RegisterUtils.binder + dto.getPhone();
Object codeTmp = redisTemplate.opsForValue().get(keyTmp);
/*校验验证码是否失效*/
if (codeTmp == null) {
throw new MyException("验证码已经失效,请重新获取");
}
if (!dto.getVerifyCode().equalsIgnoreCase(codeTmp.toString().split(":")[0])) {
throw new MyException("验证码不一致");
}
/*利用手机号在t_usr表进行查询,看是否有这个user
* 为什么不查t_logininfo表,是因为要查的话还有加一个type建,这个前端的,后端没有*/
User user = userMapper.loadByPhone(dto.getPhone());
Logininfo logininfo = null;
if (user == null) {
/*就要先进行保存这个创建一个user表*/
user = dto2User(dto);
/*这里把模板usercopy给logininfo*/
logininfo = user2Logininfo(user);
logininfoMapper.save(logininfo);
System.out.println(logininfo.getId());
/*user还有一个关于logininfo的外键*/
user.setLogininfoId(logininfo.getId());
userMapper.save(user);
}
/*进行创建一个wxuser表*/
/*当了这里是怎么样都会有一个user表生成的*/
/*利用http 根据token + openId 发送到微信进行拿到数据*/
String getWxUSER = RegisterUtils.getWxUSER
.replace("ACCESS_TOKEN", dto.getAccessToken()
.replace("OPENID", dto.getOpenId()));
/*根据这个路径去wx那用户的资源*/
String httpGet = HttpClientUtils.httpGet(getWxUSER);
/*利用JSONOBject工具进行转换为对象和接受*/
Wxuser wxuser = JSONObject.parseObject(httpGet, Wxuser.class);
/*进行这个wxuser表外键关于user外键的填入*/
wxuser.setUserId(user.getId());
/*把这个wxuser进行保存到数据库*/
wxuserMapper.save(wxuser);
/*设置昵称*/
user.setUsername(wxuser.getNickname());
userMapper.update(user);
logininfo.setUsername(wxuser.getNickname());
logininfoMapper.update(logininfo);
/*在进行免密登录*/
Logininfo logininfoTmp = logininfoMapper.loadByUserId(wxuser.getUserId());
/*String token = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(token, logininfoTmp, 30, TimeUnit.MINUTES);
*//*在进行把这个k值和v值封装成map给前端返回回去*//*
Map<String, Object> map1 = new HashMap<>();
map1.put("token", token);
logininfoTmp.setSalt(null);
logininfoTmp.setPassword(null);
map1.put("logininfo", logininfoTmp);
return map1;*/
return loginSuccessJwtHandler(logininfoTmp);
}
当这里不管是第一次还是第n次的对于的步骤都有
以下是所有的大致的步骤流程图
图太长分为两节截图
完整的图在下面的链接
https://www.processon.com/diagraming/62f60df1079129071a2b366f