前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >模仿淘宝web扫码登录

模仿淘宝web扫码登录

作者头像
用户6182664
发布2019-09-10 15:29:18
1.3K0
发布2019-09-10 15:29:18
举报

#### 首页登录:

点击右上角二维码登录切换,触发js事件。

```

//切换登录

$('#J-qrcode-target').click(function(e){

e.preventDefault();

$(this).toggleClass("scan-no");

if ($(this).hasClass('qrcode-target-show')){

$(this).removeClass('qrcode-target-show').addClass('qrcode-target-hide');

$('.scan-login').show();

$('.input-login').hide();

_this.ST && clearInterval(_this.ST);

createQRcode();

$('.j_qr_invalid').hide();

} else {

$(this).removeClass('qrcode-target-hide').addClass('qrcode-target-show');

$('.scan-login').hide();

$('.input-login').show();

_this.ST && clearInterval(_this.ST);

}

});

```

切换样式,进入扫码登录页。

请求服务端接口,获取唯一二维码code,UUID。

```

//生成

var createQRcode = function(){

$.ajax({

type : "GET",

url : ctx + "/login/do_generate_qrcode.htm",

dataType : "json",

data:{},

beforeSend:function(){

},

success : function(json) {

if (json.success) {

_this.uuid = json.datas.obj;

$('#qrcode_output').empty();

try{

$('#qrcode_output').qrcode({width:160,height:160,text:_this.uuid});

}catch(e){

$('#qrcode_output').qrcode({render : "table",width:160,height:160,text:_this.uuid});

}

scanTrace();

} else {

Utils.Notice.error(json.message);

}

},

complete:function(){

}

});

```

服务端会随机生成UUID唯一码,并将空的二维码扫描信息存入缓存。缓存设置有效期为固定的两分钟,两分钟内为扫描,该缓存会定时清空。随后,将UUID串返回给前端。

```

/**

* 二维码内容生成

*/

@ResponseBody

@RequestMapping("do_generate_qrcode")

@UrlName("用户-[登录]二维码内容生成")

public JsonResult doGenerateQRCode(){

JsonResult json = new JsonResult();

String uuid = UUID.randomUUID().toString().replace("-", "") + "_"

+System.currentTimeMillis();

CacheUtils.set(UserCst.LOGIN_QRCODE_PREFIX + uuid,

UserCst.LOGIN_QRCODE_TIMEOUT, new ScanLoginDTO());

json.addDatas(ControllerCst.JSON_KEY_OBJ, uuid);

return json;

}

```

返回给前端会显示为UUID对应的二维码:

```

//扫描跟踪

var scanTrace = function(){

_this.ST = setInterval(function(){

$.ajax({

type : "POST",

url : ctx + "/login/do_qrcode_check.htm",

dataType : "json",

data:{uuid:_this.uuid},

beforeSend:function(){

},

success : function(json) {

//扫描状态 1-未扫描 2-已扫描,待确认 3-确认登录 4-取消登录

if (json.code == '2') {

$('.j_cf_tips').show();

$('.j_scan_tips').hide();

} else if (json.code == '3') {

_this.ST && clearInterval(_this.ST);

location.href = json.datas.obj;

} else if (json.code == '4' || json.code == '105') {

$('.j_qr_invalid').show();

_this.ST && clearInterval(_this.ST);

}

},

complete:function(){

}

});

},2000);

}

```

调用生成二维码的服务时,会同步调用扫描跟踪方法。该方法会定时,每两秒请求一下服务端的do_qrcode_check方法,校验扫描状态。当未扫描时,服务端缓存失效后会返回给前端,告知该二维码已失效。

```

/**

* 二维码扫描连接检查

*/

@ResponseBody

@RequestMapping("do_qrcode_check")

@UrlName("用户-[登录]二维码扫描连接检查")

public JsonResult doQRcodeCheck(@RequestParam("uuid")String uuid, HttpServletRequest request, HttpServletResponse response){

JsonResult json = new JsonResult();

if (StringUtils.isEmpty(uuid)){

json.setSysErr();

return json;

}

String key = UserCst.LOGIN_QRCODE_PREFIX + uuid;

ScanLoginDTO scanLoginDTO = CacheUtils.get(key);

if (scanLoginDTO == null){

json.setErr(UserErrs.User.SCAN_LOGIN_QR_INVALID);

return json;

}

if (scanLoginDTO.isScanLoginOK()){

SessionUserDTO session = userCheck.getUserByLoginName(scanLoginDTO.getLoginName());

if (session == null) {

json.setErr(UserErrs.User.SCAN_LOGIN_QR_INVALID);

return null;

}

String[] ticket = TicketUtils.getTicket(String.valueOf(session.getUserId()),true);

session.setToken(ticket[0]);

//刷新session

SessionManager.createSession(session.getUserId(), SessionCst.SESSION_ATTRIBUTE_USER_POJO, session);

LoginPostProcessor.addCookiesForTicket(request, response, ticket[1]);

LoginPostProcessor.addCookiesForNick(request, response, session.getNick());

String redirectUrl = WebUtils.getRoleUrl(String.valueOf(session.getRoleId()));

json.addDatas(ControllerCst.JSON_KEY_OBJ, redirectUrl);

} else if (scanLoginDTO.isCanLoginCancel()) {

CacheUtils.delete(key);

}

json.setCode(String.valueOf(scanLoginDTO.getStatus()));

return json;

}

```

服务端将扫描状态返回,告知前端该二维码使用扫描状态,当缓存到期后,会告知前端二维码已失效。

当用户在有效期内,扫描二维码时,会请求服务端接口。调用scan_request方法,将获取到的uuid,以及移动端自己的当前登录用户的loginName以及登录后的ticket传递给服务端进行记录。

```

/**

* 扫描登录请求

*

*/

@ResponseForMobile

@RequestMapping("scan_request")

@UrlName("用户-[手机登录接口]扫描登录请求")

public JsonResult scanRequest(@RequestParam(value = ControllerCst.DATA) String data) {

JsonResult json = new JsonResult();

APIParamModel<ScanLoginDTO> mode = _parse(data, new TypeReference<APIParamModel<ScanLoginDTO>>() {});

ScanLoginDTO scanLoginDTO = mode.getP();

_check(scanLoginDTO.getUuid(), "uuid");

_check(scanLoginDTO.getLoginName(), "loginName");

_check(scanLoginDTO.getTicket(), "ticket");

SessionUserDTO session = UserContext.user.get();

String key = UserCst.LOGIN_QRCODE_PREFIX + scanLoginDTO.getUuid();

ScanLoginDTO cacheScanLoginDTO = CacheUtils.get(key);

if (cacheScanLoginDTO == null || cacheScanLoginDTO.isCanLoginCancel()){

json.setErr(UserErrs.User.SCAN_LOGIN_QR_INVALID);

return json;

}

if (cacheScanLoginDTO.isScanWaitConfirm() || cacheScanLoginDTO.isScanLoginOK()) {

if (!cacheScanLoginDTO.getLoginName().equals(session.getLoginName())) {

json.setErr(UserErrs.User.SCAN_LOGIN_QR_INVALID);

return json;

}

}

CacheUtils.set(key, scanLoginDTO);

return json;

}

```

服务端会判断缓存中是否存在该UUID对应的缓存,并且确认状态是否是未确认,是否在可确认状态。校验后,会将信息放置缓存,等待用户发出确认登录请求。

当用户点击确认登录时,会给服务端发送确认登录请求。

```

/**

* 确认登录请求

*/

@ResponseForMobile

@RequestMapping("scan_confirm")

@UrlName("用户-[手机登录接口]确认登录请求")

public JsonResult scanConfirm(@RequestParam(value = ControllerCst.DATA) String data) {

JsonResult json = new JsonResult();

APIParamModel<ScanLoginDTO> mode = _parse(data, new TypeReference<APIParamModel<ScanLoginDTO>>() {});

ScanLoginDTO scanLoginDTO = mode.getP();

_check(scanLoginDTO.getUuid(), "uuid");

_check(scanLoginDTO.getStatus(), "status");

_check(scanLoginDTO.getLoginName(), "loginName");

_check(scanLoginDTO.getTicket(), "ticket");

SessionUserDTO session = UserContext.user.get();

String key = UserCst.LOGIN_QRCODE_PREFIX + scanLoginDTO.getUuid();

ScanLoginDTO cacheScanLoginDTO = CacheUtils.get(key);

if (cacheScanLoginDTO == null || cacheScanLoginDTO.isCanLoginCancel()){

json.setErr(UserErrs.User.SCAN_LOGIN_QR_INVALID);

return json;

}

if (cacheScanLoginDTO.isScanWaitConfirm() || cacheScanLoginDTO.isScanLoginOK()) {

if (!cacheScanLoginDTO.getLoginName().equals(session.getLoginName())) {

json.setErr(UserErrs.User.SCAN_LOGIN_QR_INVALID);

return json;

}

}

CacheUtils.set(key, scanLoginDTO);

return json;

}

```

服务端会根据用户发送的参数,校验二维码登录缓存是否存在,以及校验移动端当前session中的登录名是否与传递的登录名参数是否一致,避免移动端恶意攻击,违规登录别人的账号。一切确认好以后,将缓存中的uuid回应的扫码信息绑定至缓存中。

随后前端定时每两秒会请求的js会发送请求,确认用户登录的状态。

```

//扫描跟踪

var scanTrace = function(){

_this.ST = setInterval(function(){

$.ajax({

type : "POST",

url : ctx + "/login/do_qrcode_check.htm",

dataType : "json",

data:{uuid:_this.uuid},

beforeSend:function(){

},

success : function(json) {

//扫描状态 1-未扫描 2-已扫描,待确认 3-确认登录 4-取消登录

if (json.code == '2') {

$('.j_cf_tips').show();

$('.j_scan_tips').hide();

} else if (json.code == '3') {

_this.ST && clearInterval(_this.ST);

location.href = json.datas.obj;

} else if (json.code == '4' || json.code == '105') {

$('.j_qr_invalid').show();

_this.ST && clearInterval(_this.ST);

}

},

complete:function(){

}

});

},2000);

}

```

服务端执行连接检查接口,当登录状态完成正常以后,会获取用户登录信息ticket,设置token,以及为用户创建session.根据用户session中的roleID,返回用户登录成功后应该跳转的页面。

```

/**

* 二维码扫描连接检查

*/

@ResponseBody

@RequestMapping("do_qrcode_check")

@UrlName("用户-[登录]二维码扫描连接检查")

public JsonResult doQRcodeCheck(@RequestParam("uuid")String uuid, HttpServletRequest request, HttpServletResponse response){

JsonResult json = new JsonResult();

if (StringUtils.isEmpty(uuid)){

json.setSysErr();

return json;

}

String key = UserCst.LOGIN_QRCODE_PREFIX + uuid;

ScanLoginDTO scanLoginDTO = CacheUtils.get(key);

if (scanLoginDTO == null){

json.setErr(UserErrs.User.SCAN_LOGIN_QR_INVALID);

return json;

}

if (scanLoginDTO.isScanLoginOK()){

SessionUserDTO session = userCheck.getUserByLoginName(scanLoginDTO.getLoginName());

if (session == null) {

json.setErr(UserErrs.User.SCAN_LOGIN_QR_INVALID);

return null;

}

String[] ticket = TicketUtils.getTicket(String.valueOf(session.getUserId()),true);

session.setToken(ticket[0]);

//刷新session

SessionManager.createSession(session.getUserId(), SessionCst.SESSION_ATTRIBUTE_USER_POJO, session);

LoginPostProcessor.addCookiesForTicket(request, response, ticket[1]);

LoginPostProcessor.addCookiesForNick(request, response, session.getNick());

String redirectUrl = WebUtils.getRoleUrl(String.valueOf(session.getRoleId()));

json.addDatas(ControllerCst.JSON_KEY_OBJ, redirectUrl);

} else if (scanLoginDTO.isCanLoginCancel()) {

CacheUtils.delete(key);

}

json.setCode(String.valueOf(scanLoginDTO.getStatus()));

return json;

}

```

密码显示与隐藏:

//显示密码

```

$("#eye").click(function(e){

e.preventDefault();

var type = $(this).siblings("input").get(0).getAttribute("type");

var pwd = $(this).siblings("input").get(0)

if(type == "password"){

$("#eye span").removeClass("focuse").addClass("focus-out");

pwd.setAttribute("type","text")

}else{

$("#eye span").removeClass("focus-out").addClass("focuse")

pwd.setAttribute("type","password")

}

});

```

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java程序员那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档