APP扫码登录完整实现:从原理到代码的全栈开发指南
APP扫码登录:不只有原理,直接上代码 一.背景 扫码登录前提是要有APP端,和PC端。其目的是为了让用户在使用的PC端时登录更加方便和安全,使用手机扫一扫就可以登录和使用服务。 还有一个重要原因就是引导用户下载APP。 前段时间正好接到这么一个需求,设计和相关代码都会在本文贴出来。 话不多说,进入正题。 二.原理和时序图 二维码状态 INIT(1, “初始状态”), SCANNING(2, “扫码中”), CANCEL(3, “取消”), CONFIRM(4, “确定登录”), EXPIRE(5, “过期”); 用户在PC端请求二维码信息 服务端初始化二维码信息 生成二维码唯一的oauthKey,存储到Redis,设置过期时间,这里我配置的60S,过期时间看自己需求。 这时候二维码的状态是 INIT 服务端返回二维码信息给PC端 返回约定好的信息给PC端,我们这里还有APP的跳转链接。 PC端拿到二维码信息后生成二维码展示给用户 PC端定时轮询二维码信息 PC端根据二维码的状态做想应的处理,比如: 扫码中 -> 置灰二维码, 取消 -> 弹框提示和刷新二维码 确定登录 -> 拿到token,调取用户信息接口,取消二维码页面 过期 -> 弹框提示和刷新二维码 服务端返回此二维码的状态和登录信息 用户使用APP扫描二维码 APP解析二维码,获取oauthkey APP使用oauthkey校验二维码信息 查询二维码信息是否有效,是否过期 返回校验结果给到APP APP根据校验结果,有效就弹出确认框或者取消框,无效就弹框提示 确认登录 用户在APP上点击确认登录按钮,确认登录 服务端登录逻辑 更新二维码状态为确认登录 判断APP的用户的登录状态 判断登录的设备个数 生成token , 记录想应的缓存信息 第4步的轮询接口返回登录token PC端使用token请求获取用户信息,和其他接口 登录完成 三.代码 controller @Slf4j @RestController @RequestMapping(value = "/user/qrcode") @Api(tags = "WEB端扫码API") public class ScanQrcodeLoginController { @Autowired private ScanQrcodeLoginService scanQrcodeLoginService; /** * 获取二维码链接和信息 */ @ApiOperation(value = "获取二维码链接和信息") @GetMapping(value = "/info") public ResultResponse<LoginUrlVO> getQrcodeInfo(){ return ResultResponse.success(scanQrcodeLoginService.getQrcodeInfo()); } /** * 获取二维码扫描状态 */ @ApiOperation(value = "获取二维码扫描状态") @GetMapping(value = "/state") public ResultResponse<QrcodeStateVO> getQrcodeState(@RequestParam(value = "oauthKey") String oauthKey){ return ResultResponse.success(scanQrcodeLoginService.getQrcodeState(oauthKey)); } } @Slf4j @RestController @RequestMapping(value = "/user/app/qrcode") @Api(tags = "APP端扫码API") public class AppScanQrcodeLoginController extends BaseController { @Autowired private ScanQrcodeLoginService scanQrcodeLoginService; /** * 二维码扫描 */ @ApiOperation(value = "二维码扫描") @GetMapping(value = "/scan") public ResultResponse<QrcodeScanResultVO> scan(@RequestParam(value = "oauthKey") String oauthKey){ Long userId = getCurrentUserId(false); return ResultResponse.success(scanQrcodeLoginService.scan(oauthKey, userId)); } /** * 二维码取消 */ @ApiOperation(value = "二维码取消") @GetMapping(value = "/cancel") public ResultResponse<QrcodeCancelResultVO> cancel(@RequestParam(value = "oauthKey") String oauthKey){ Long userId = getCurrentUserId(true); return ResultResponse.success(scanQrcodeLoginService.cancel(oauthKey, userId)); } /** * 二维码确定登录 */ @ApiOperation(value = "二维码确定登录") @GetMapping(value = "/confirm") public ResultResponse<QrcodeConfirmResultVO> confirm(@RequestParam(value = "oauthKey") String oauthKey){ Long userId = getCurrentUserId(true); return ResultResponse.success(scanQrcodeLoginService.confirm(oauthKey, userId)); } } @Slf4j @RestController @RequestMapping(value = "/user/web/login") @Api(tags = "WEB端-登录接口") public class WebLoginController extends BaseController { @Autowired private WebUserLoginService webUserLoginService; /** * 获取登录用户信息 */ @ApiOperation(value = "获取登录用户信息") @GetMapping(value = "/info") public ResultResponse<WebUserInfoVo> getWebUserInfo() { Long userId = getCurrentUserId(true); return ResultResponse.success(webUserLoginService.getWebUserInfo(userId)); } /** * 退出登录 */ @ApiOperation(value = "退出登录") @GetMapping(value = "/logout") public ResultResponse<Void> webLogout() throws UserRequestParameterException { Long userId = getCurrentUserId(true); String token = HttpUtils.getToken(); log.info("web logout userId: {}, token: {}", userId, token); webUserLoginService.webLogout(userId, token); return ResultResponse.success(); } } service @Service @Slf4j public class ScanQrcodeLoginServiceImpl implements ScanQrcodeLoginService { @Autowired private OauthKeyHelp oauthKeyHelp; @Autowired private WebLoginConfig webLoginConfig; @Autowired private RedissonClient redissonClient; @Override public LoginUrlVO getQrcodeInfo() { String oauthKey = UUID.randomUUID().toString().replaceAll("-", ""); LoginUrlVO loginUrlVO = new LoginUrlVO(); loginUrlVO.setOauthKey(oauthKey); loginUrlVO.setUrl(webLoginConfig.getQrcodeUrl() + oauthKey); oauthKeyHelp.init(oauthKey); return loginUrlVO; } @Override public QrcodeStateVO getQrcodeState(String oauthKey) { QrcodeStateVO stateVO = new QrcodeStateVO(); if (!oauthKeyHelp.verify(oauthKey)) { stateVO.setStatus(QrcodeStatus.EXPIRE.getStatus()); return stateVO; } OauthTicket oauthTicket = oauthKeyHelp.getStatus(oauthKey); if (Objects.isNull(oauthTicket)) { stateVO.setStatus(QrcodeStatus.EXPIRE.getStatus()); return stateVO; } stateVO.setStatus(oauthTicket.getStatus()); stateVO.setJwtToken(oauthTicket.getToken()); stateVO.setUserId(oauthTicket.getUserId()); return stateVO; } @Override public QrcodeScanResultVO scan(String oauthKey, Long userId) { QrcodeScanResultVO resultVO = new QrcodeScanResultVO(); if (!oauthKeyHelp.verify(oauthKey)) { resultVO.setStatus(Boolean.FALSE); return resultVO; } oauthKeyHelp.scan(oauthKey, userId); resultVO.setStatus(Boolean.TRUE); return resultVO; } @Override public QrcodeCancelResultVO cancel(String oauthKey, Long userId) { QrcodeCancelResultVO resultVO = new QrcodeCancelResultVO(); if (!oau
以上内容来自博客订阅源,版权归原作者。热度统计站内详情页访问。

登录后,加入这场讨论。
登录参与