扫码登录-客户端定时器轮训实现
controller接口
详细可参考源代码。cn.codersnet.helloworld.scanlogin.web.ScanLoginController
@RestController
@RequestMapping("/scanlogin")
public class ScanLoginController {
private Log logger = LogFactory.getLog(this.getClass());
private static final String TOKEN_LOGINID_SUFFIX = "_loginID";
@Autowired
private Cache cache;
/**
* 生成二维码,暂时返回token
* @return
*/
@GetMapping("/generate")
public String generateCrCode() {
//生成token
String token = UUID.randomUUID().toString();
//将token置于缓存中,可以设置默认有效期,可以直接使用redis缓存设置过期时间。这里简单通过map实现
cache.put(token, new ScanStatus(ScanStatus.CODE_GENERATE, "二维码已生成, 待扫描"), 60); //60s后过期
return token;
}
/**
* 移动端扫码
* @return
*/
@GetMapping("/appScan")
public ScanStatus appScan(String token) {
ScanStatus status = new ScanStatus(ScanStatus.CODE_SCANED, "二维码已扫描, 待确认");
if(cache.existed(token)) {
cache.put(token, status);
} else {
status.setStatusCode(ScanStatus.CODE_EXPIRED);
status.setMessage("二维码已过期或不存在");
}
return status;
}
/**
* 移动端确认登录,如果存在token未过期,设置token和loginID关联关系
* @param token
* @param loginID
* @return
*/
@GetMapping("/appConfirm")
public ScanStatus appConfirm(String token, String loginID) {
ScanStatus status = new ScanStatus(ScanStatus.CODE_APP_CONFIRM, "移动端已确认");
if(cache.existed(token)) {
cache.put(token, status);
cache.put(token + TOKEN_LOGINID_SUFFIX, loginID);
} else {
status.setStatusCode(ScanStatus.CODE_EXPIRED);
status.setMessage("二维码已过期或不存在");
}
return status;
}
/**
* 检查扫码状态
* @param token
* @return
*/
@PostMapping("/check")
public ScanStatus check(String token) {
if(cache.existed(token)) {
ScanStatus scanStatus = (ScanStatus)cache.get(token);
if(ScanStatus.CODE_APP_CONFIRM == scanStatus.getStatusCode()) {
//TODO 执行登录
String loginID = (String)cache.get(token + TOKEN_LOGINID_SUFFIX);
logger.info(loginID + ", app扫描登录成功");
return new ScanStatus(ScanStatus.CODE_LOGIN_SUCCESS, "登录成功");
} else {
return scanStatus;
}
}
//二维码过期
cache.remove(token + TOKEN_LOGINID_SUFFIX);
return new ScanStatus(ScanStatus.CODE_EXPIRED, "二维码已过期或不存在");
}
}
单元测试
详细可参考源代码。cn.codersnet.helloworld.ScanLoginTests
@RunWith(SpringRunner.class)
@SpringBootTest(classes = HelloworldApplication.class)
@AutoConfigureMockMvc
public class ScanLoginTests {
@Autowired
private MockMvc mvc;
private JacksonTester<ScanStatus> json;
@Before
public void setup() {
ObjectMapper objectMapper = new ObjectMapper();
JacksonTester.initFields(this, objectMapper);
}
/**
* 扫码登录测试测试方法
* @throws Exception
*/
@Test
public void scanLoginSuccessTest() throws Exception {
// 1. 获取token(二维码)
String token = mvc.perform(get("/scanlogin/generate")).andReturn().getResponse().getContentAsString();
Assert.isTrue(check(token).getStatusCode() == ScanStatus.CODE_GENERATE, "生成二维码失败");
// 2. 移动端扫码
mvc.perform(get("/scanlogin/appScan").param("token", token));
Assert.isTrue(check(token).getStatusCode() == ScanStatus.CODE_SCANED, "扫码失败");
// 3. 移动端确认登录
mvc.perform(get("/scanlogin/appConfirm").param("token", token).param("loginID", "test01"));
// 4. PC端登录成功
Assert.isTrue(check(token).getStatusCode() == ScanStatus.CODE_LOGIN_SUCCESS, "登录失败");
}
/**
* 扫码登录超时测试方法,示例代码中超时时间为60s
* @throws Exception
*/
@Test
public void scanLoginTimeoutTest() throws Exception {
// 1. 获取token(二维码)
String token = mvc.perform(get("/scanlogin/generate")).andReturn().getResponse().getContentAsString();
Assert.isTrue(check(token).getStatusCode() == ScanStatus.CODE_GENERATE, "生成二维码失败");
//2. 等待超时
Thread.sleep(60*1000);
//3. PC端check验证
Assert.isTrue(check(token).getStatusCode() == ScanStatus.CODE_EXPIRED, "超时校验失败");
//4. 移动端扫码验证
mvc.perform(get("/scanlogin/appScan").param("token", token));
Assert.isTrue(check(token).getStatusCode() == ScanStatus.CODE_EXPIRED, "超时校验失败");
}
private ScanStatus check(String token) throws Exception {
String checkStr = mvc.perform(post("/scanlogin/check").param("token", token)).andReturn().getResponse()
.getContentAsString();
return this.json.parseObject(checkStr);
}
}
网友评论