文章目录
- 一.技术以及框架选择
- 二.功能需求
- 三.功能具体实现
- 1.登录流程
- 2.代码实现
- 2.1 小程序登录事件
- 2.2 登录逻辑
- 2.3 后台登录逻辑实现
- 2.3.1 登录接口
最近接到小程序毕设业务业务,记录下开发过程,UI实现这些就没必要写了
记录一下登录功能实现,虽然很简单不值一提.
一.技术以及框架选择
后台开发框架:springboot
权限框架:spring Security
小程序开发:uni-app
二.功能需求
1.点击登录,后台签发token,之后每次请求都携带这个token以此鉴权,没有相关权限不给访问
三.功能具体实现
1.登录流程
第一步: 调用登录API,wx.login获得code,并将code发送到我们的后台
第二步: 后台接收到code后,再向微信登录凭证校验接口发起请求,携带参数有:小程序appId,AppSecret(小程序密钥)和刚拿到的code,参数没错便返回openId和session_key,参数错误
的话才会返回err相关数据(开始以为正确与否都会返回,结果不是)
第三步: 拿到返回来的相关数据与小程序前端传递过来的用户数据(头像昵称等)一起保存进数据库
第四步:签发登录token,并返回到小程序,小程序保存到Storage和vuex
2.代码实现
2.1 小程序登录事件
<button @click="loginAction" :plain="true" class="user-name" style="font-size: 35upx;border: none;">点击登录,记录点点滴滴..></button>
不再使用了:开发能力: open-type=“getUserInfo” -> 获取用户账号信息,昵称头像等
函数: @getuserinfo=“login” -> 获取用户账号信息的回调函数,并在这里做登录逻辑实现
更新的代码
wx.getUserProfile 的API,详情请看官方文档
2.2 登录逻辑
这里将userInfo和code一起传到后台去,就可以完成登录同时保存用户数据
//微信用户登录
loginAction(){
var that = this;
wx.getUserProfile({
desc: "获取你的昵称、头像、地区及性别",
success: res => {
console.log("新版userInfo",res)
var userinfo = res.userInfo;
uni.login({
provider: 'weixin',
success: function (loginRes) {
uni.showLoading({
mask:true,
title:"加载中..."
})
console.log("登录code",loginRes);
// 获得登录凭证code,将其传到后台,进行最终的登录操作获取openId
if(loginRes.code){
userinfo['code'] = loginRes.code
that.$http.post('/app/applet/login',userinfo).then(resp=>{
//将数据保存在状态管理器中
that.$store.commit("setUserInfo",resp.data) //
//将数据保存在用户的手机内存中
uni.setStorage({
key: 'userInfo',
data: resp.data,
success: function () {
uni.hideLoading()
}
});
}).catch(err=>{
console.log("登录失败");
uni.showModal({
content:"登录失败",
showCancel:false
})
uni.hideLoading()
})
}
uni.hideLoading()
}
});
},
fail: res => {
//拒绝授权
// that.showErrorModal('您拒绝了请求');
return;
}
})
},
2.3 后台登录逻辑实现
2.3.1 登录接口
/**
* auth.code2Session
* https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
* 返回值
* @return JSON 数据包
* 属性 类型 说明
* openid string 用户唯一标识
* session_key string 会话密钥
* unionid string 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
* errcode number 错误码
* errmsg string 错误信息
*
* errcode 的合法值
*
* 值 说明 最低版本
* -1 系统繁忙,此时请开发者稍候再试
* 0 请求成功
* 40029 code 无效
* 45011 频率限制,每个用户每分钟100次
*/
@PostMapping("login")
public ResponseData login(@RequestBody JSONObject userInfo){
System.out.println("登录"+userInfo.getString("code"));
VAppletDTO appletDTO = vAppletService.get(); //获取小程序配置
JSONObject jsonObject = WXUtils.authCode2Session(appletDTO.getAppId(), appletDTO.getAppSecret(), userInfo.getString("code"));
if(jsonObject.get("errcode")!=null){
//登录失败
return new ResponseData().fail(ResponseCode.USER_LOGIN_FAIL.getCode(),"登录失败");
}
//登录成功,
String openid = jsonObject.getString("openid");
VUserDTO byOpenId = vUserService.getByOpenId(openid); //根据openId查询数据库中是否有这个用户
VUserEntity entity =null;
if(byOpenId==null){//没有当前登录用户,保存新用户数据
byOpenId = new VUserDTO();
//设置新数据
byOpenId.setAvatar(userInfo.getString("avatarUrl"));
byOpenId.setNickName(userInfo.getString("nickName"));
byOpenId.setOpenId(openid);
byOpenId.setUnionId(jsonObject.getString("unionid"));
byOpenId.setSeesionKey(jsonObject.getString("session_key"));
byOpenId.setGender(userInfo.getIntValue("gender"));
byOpenId.setCountry(userInfo.getString("country"));
byOpenId.setProvince(userInfo.getString("province"));
byOpenId.setCity(userInfo.getString("city"));
entity = vUserService.save(byOpenId);//将数据存进数据库
//生成登录成功的token令牌
}else {
//设置新数据
byOpenId.setAvatar(userInfo.getString("avatarUrl"));
byOpenId.setNickName(userInfo.getString("nickName"));
byOpenId.setOpenId(openid);
byOpenId.setUnionId(jsonObject.getString("unionid"));
byOpenId.setSeesionKey(jsonObject.getString("session_key"));
vUserService.update(byOpenId);
entity = ConvertUtils.sourceToTarget(byOpenId,VUserEntity.class);
}
//将权限放/角色入token中
Map<String, Object> map = new HashMap<>();
map.put(ResponseCode.ParamterUserIdKey,entity.getId());
map.put("role","ROLE_user"); //默认小程序用户属于"user" 用户组,
map.put("nickName",entity.getNickName());
String token = null;
try {
token = XTokenUtils.createToken(map, ResponseCode.TokenExp);
UserVO userVO = ConvertUtils.sourceToTarget(byOpenId, UserVO.class);
userVO.setToken(token);
return new ResponseData().success("登录成功",userVO);
} catch (Exception e) {
e.printStackTrace();
}
return new ResponseData().fail("登录失败");
}
注: spring Security用角色控制的记得加ROLE_前缀,
微信请求工具类
/**
* @description: 微信请求工具类
* @author: ※狗尾巴草
* @date: 2021-03-22 22:57
**/
public class WXUtils {
/**
* auth.code2Session
* https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
* 请求参数 属性 类型 默认值 必填 说明
* @param appId string 是 小程序 appId
* @param secret string 是 小程序 appSecret
* @param jsCode string 是 登录时获取的 code
* grantType string 是 授权类型,此处只需填写 authorization_code
* 返回值
* @return JSON 数据包
* 属性 类型 说明
* openid string 用户唯一标识
* session_key string 会话密钥
* unionid string 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
* errcode number 错误码
* errmsg string 错误信息
*
* errcode 的合法值
*
* 值 说明 最低版本
* -1 系统繁忙,此时请开发者稍候再试
* 0 请求成功
* 40029 code 无效
* 45011 频率限制,每个用户每分钟100次
*/
public static JSONObject authCode2Session(String appId, String secret, String jsCode) {
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + secret + "&js_code=" + jsCode + "&grant_type=authorization_code";
JSONObject jsonObject = Httprequest.get(url);
//System.out.println(jsonObject);
return jsonObject;
}
}
简单写了个Httprequest工具类
/**
* @description: Httprequest工具类
* @author: ※狗尾巴草
* @date: 2021-03-22 23:01
**/
public class Httprequest {
/**
* @description: http请求--post请求
* @time: 2019/12/22 0:11
*/
public static JSONObject post(String url,Map<String, Object> data){
HttpRequest httpRequest = new HttpRequest(url, "POST");
httpRequest.form(data);
return JSONObject.parseObject(httpRequest.body());
}
/**
* @description: http请求--get请求
* @time: 2019/12/22 0:11
*/
public static JSONObject get(String url){
return JSONObject.parseObject(HttpRequest.get(url).body());
}
}
http工具的依赖
<dependency>
<groupId>com.github.kevinsawicki</groupId>
<artifactId>http-request</artifactId>
<version>5.6</version>
</dependency>
在登录接口实现那里完成了tokan签发,最后添加权限拦截规则
来到security的配置类
.antMatchers("/user/**").hasAnyRole("user")
如此便完成了登录以及权限控制了