#### 首页登录:
点击右上角二维码登录切换,触发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")
}
});
```