java编程之基于SpringBoot框架实现扫码登录
完整代码已上传到github。
web
端体验地址:http://47.116.72.33/(只剩一个月有效期)
apk
下载地址:。
用户名:非空即可,密码:123456,效果见文末,整体实现如有不妥之处,欢迎交流讨论
实现部分参考。
项目简介
后端:springboot
,redis
。
前端:vue
,vue router
、vuex
、axios
、vue-qr
、elemntui
。
安卓:zxing
、xui
、yhttp
。
实现思路
总体的扫码登录和oauth2.0
的验证逻辑相似,如下所示:
用户选择扫码登录可以看作是a
:前端发授权请求,等待app
扫码。
用户使用app
进行扫码可以看作是b
:扫码进行授权,返回一个临时token
供二次认证。
用户在app
进行确认登录可以看作是c
:进行登录确认,授权用户在web
端登录。
后端在用户确认登录后返回一个正式token
即可看作是步骤d
。
后续前端根据正式token
访问后台接口,正式在web
端进行操作即可看作是e
和f
。
二次认证的原因
之所以在用户扫码之后还需要进行再一次的确认登录,而不是直接就登录的原因,则是为了用户安全考虑,避免用户扫了其他人需要登录的二维码,在未经确认就直接登录了,导致他人可能会在我们不知道的情况下访问我们的信息。
实现步骤
用户访问网页端,选择扫码登录
用户在选择扫码登录时,会向后端发送一个二维码的生成请求,后端生成uuid
,并保存到redis
(固定有效时间),状态设置为unused
(未使用)状态,如果redis
缓存过期,则为expire
(过期)状态,前端根据后端返回的内容生成二维码,并设置一个定时器,每隔一段时间根据二维码的内容中的uuid
,向后端发送请求,获取二维码的状态,更新界面展示的内容。
生成二维码后端接口:
/** * 生成二维码内容 * * @return 结果 */ @getmapping("/generate") public baseresult generate() { string code = idutil.simpleuuid(); rediscache.setcacheobject(code, codeutils.getunusedcodeinfo(), default_qr_expire_seconds, timeunit.seconds); return baseresult.success(generate_success, code); }
前端获取内容,生成二维码:
gettoken() { this.codestatus = 'empty' this.tip = '正在获取登录码,请稍等' // 有效时间 60 秒 this.effectiveseconds = 60 clearinterval(this.timer) request({ method: 'get', url: '/code/generate' }).then((response) => { // 请求成功, 设置二维码内容, 并更新相关信息 this.code = `${host}/code/scan?code=${response.data}` this.codestatus = 'unused' this.tip = '请使用手机扫码登录' this.timer = setinterval(this.gettokeninfo, 2000) }).catch(() => { this.gettoken() }) }
后端返回二维码状态信息的接口:
/** * 获取二维码状态信息 * * @param code 二维码 * @return 结果 */ @getmapping("/info") public baseresult info(string code) { codevo codevo = rediscache.getcacheobject(code); if (codevo == null) { return baseresult.success(invalid_code, stringutils.empty); } return baseresult.success(get_success, codevo); }
前端轮询获取二维码状态:
gettokeninfo() { this.effectiveseconds-- // 二维码过期 if (this.effectiveseconds <= 0) { this.codestatus = 'expire' this.tip = '二维码已过期,请刷新' return } // 轮询查询二维码状态 request({ method: 'get', url: '/code/info', params: { code: this.code.substr(this.code.indexof('=') + 1) } }).then(response => { const codevo = response.data // 二维码过期 if (!codevo || !codevo.codestatus) { this.codestatus = 'expire' this.tip = '二维码已过期,请刷新' return } // 二维码状态为为正在登录 if (codevo.codestatus === 'confirming') { this.username = codevo.username this.avatar = codevo.avatar this.codestatus = 'confirming' this.tip = '扫码成功,请在手机上确认' return } // 二维码状态为确认登录 if (codevo.codestatus === 'confirmed') { clearinterval(this.timer) const token = codevo.token store.commit('settoken', token) this.$router.push('/home') message.success('登录成功') return } }) }
使用手机扫码,二维码状态改变
当用户使用手机扫码时(已登录并且为正确的app
,否则扫码会跳转到自定义的宣传页),会更新二维码的状态为confirming
(待确认)状态,并在redis
缓存中新增用户名及头像信息的保存供前端使用展示,此外还会返回用户的登录信息(登录地址、浏览器、操作系统)给app
展示,同时生成一个临时token
给app
(固定有效时间)。
用户扫码时的后台处理:
/** * 处理未使用状态的二维码 * * @param code 二维码 * @param token token * @return 结果 */ private baseresult handleunusedqr(string code, string token) { // 校验 app 端访问传递的 token boolean islegal = jwtutils.verify(token); if (!islegal) { return baseresult.error(authentication_failed); } // 保存用户名、头像信息, 供前端展示 string username = jwtutils.getusername(token); codevo codevo = codeutils.getconfirmingcodeinfo(username, default_avatar_url); rediscache.setcacheobject(code, codevo, default_qr_expire_seconds, timeunit.seconds); // 返回登录地址、浏览器、操作系统以及一个临时 token 给 app string address = httputils.getrealaddressbyip(); string browser = httputils.getbrowsername(); string os = httputils.getosname(); string tmptoken = jwtutils.sign(username); // 将临时 token 作为键, 用户名为内容存储在 redis 中 rediscache.setcacheobject(tmptoken, username, default_temp_token_expire_minutes, timeunit.minutes); logininfovo logininfovo = new logininfovo(address, browser, os, tmptoken); return baseresult.success(scan_success, logininfovo); }
手机确认登录
当用户在app
中点击确认登录时,就会携带生成的临时token
发送更新状态的请求,二维码的状态会被更新为confirmed
(已确认登录)状态,同时后端会生成一个正式token
保存在redis
中,前端在轮询更新状态时获取这个token
,然后使用这个token
进行登录。
后端处理确认登录的代码:
/** * 处理未待确认状态的二维码 * * @param code 二维码 * @param token token * @return 结果 */ private baseresult handleconfirmingqr(string code, string token) { // 使用临时 token 获取用户名, 并从 redis 中删除临时 token string username = rediscache.getcacheobject(token); if (stringutils.isblank(username)) { return baseresult.error(authentication_failed); } rediscache.deleteobject(token); // 根据用户名生成正式 token并保存在 redis *前端使用 string formaltoken = jwtutils.sign(username); codevo codevo = codeutils.getconfirmedcodeinfo(username, default_avatar_url, formaltoken); rediscache.setcacheobject(code, codevo, default_qr_expire_seconds, timeunit.seconds); return baseresult.success(confirm_success); }
效果演示
以上就是java编程基于springboot框架实现扫码登录的详细内容,更多关于java编程springboot框架实现扫码登录的资料请关注其它相关文章!