From 15adbf489edab6fc112c54df0253b19230197cc4 Mon Sep 17 00:00:00 2001 From: xiaochangbai <704566072@qq.com> Date: Sun, 7 Aug 2022 21:34:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20app=E6=89=AB=E7=A0=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../passport/MemberBuyerController.java | 77 +++++++++++++++++++ .../main/java/cn/lili/cache/CachePrefix.java | 11 ++- .../enums/QRCodeLoginSessionStatusEnum.java | 46 +++++++++++ .../entity/vo/QRCodeLoginSessionVo.java | 21 +++++ .../member/entity/vo/QRLoginResultVo.java | 12 +++ .../modules/member/service/MemberService.java | 10 +++ .../member/serviceimpl/MemberServiceImpl.java | 77 ++++++++++++++++++- .../member/token/MemberTokenGenerate.java | 7 +- 8 files changed, 253 insertions(+), 8 deletions(-) create mode 100644 framework/src/main/java/cn/lili/modules/member/entity/enums/QRCodeLoginSessionStatusEnum.java create mode 100644 framework/src/main/java/cn/lili/modules/member/entity/vo/QRCodeLoginSessionVo.java create mode 100644 framework/src/main/java/cn/lili/modules/member/entity/vo/QRLoginResultVo.java diff --git a/buyer-api/src/main/java/cn/lili/controller/passport/MemberBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/passport/MemberBuyerController.java index 02dfc48a..326977d8 100644 --- a/buyer-api/src/main/java/cn/lili/controller/passport/MemberBuyerController.java +++ b/buyer-api/src/main/java/cn/lili/controller/passport/MemberBuyerController.java @@ -7,6 +7,8 @@ import cn.lili.common.security.enums.UserEnums; import cn.lili.common.vo.ResultMessage; import cn.lili.modules.member.entity.dos.Member; import cn.lili.modules.member.entity.dto.MemberEditDTO; +import cn.lili.modules.member.entity.enums.QRCodeLoginSessionStatusEnum; +import cn.lili.modules.member.entity.vo.QRLoginResultVo; import cn.lili.modules.member.service.MemberService; import cn.lili.modules.sms.SmsUtil; import cn.lili.modules.verification.entity.enums.VerificationEnums; @@ -15,10 +17,18 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; import javax.validation.constraints.NotNull; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + /** * 买家端,会员接口 @@ -26,6 +36,7 @@ import javax.validation.constraints.NotNull; * @author Chopper * @since 2020/11/16 10:07 下午 */ +@Slf4j @RestController @Api(tags = "买家端,会员接口") @RequestMapping("/buyer/passport/member") @@ -39,6 +50,72 @@ public class MemberBuyerController { private VerificationService verificationService; + @ApiOperation(value = "web-获取登录二维码") + @PostMapping(value = "/pc_session", produces = "application/json;charset=UTF-8") + public ResultMessage createPcSession() { + return ResultUtil.data(memberService.createPcSession()); + } + + + + /** + * 长轮询:参考nacos + * @param token + * @param beforeSessionStatus 上次记录的session状态 + * @return + */ + @ApiOperation(value = "web-二维码登录") + @PostMapping(value = "/session_login/{token}", produces = "application/json;charset=UTF-8") + public Object loginWithSession(@PathVariable("token") String token,Integer beforeSessionStatus) { + log.info("receive login with session key {}", token); + ResponseEntity timeoutResponseEntity = + new ResponseEntity<>(ResultUtil.error(ResultCode.ERROR), HttpStatus.OK); + int timeoutSecond = 10; + DeferredResult deferredResult = new DeferredResult<>(timeoutSecond * 1000L, timeoutResponseEntity); + CompletableFuture.runAsync(() -> { + try { + int i = 0; + while (i < timeoutSecond) { + QRLoginResultVo queryResult = memberService.loginWithSession(token); + int status = queryResult.getStatus(); + if(status==beforeSessionStatus + && (QRCodeLoginSessionStatusEnum.WAIT_SCANNING.getCode()==status + || QRCodeLoginSessionStatusEnum.SCANNING.getCode()==status)){ + //睡眠一秒种,继续等待结果 + TimeUnit.SECONDS.sleep(1); + }else{ + deferredResult.setResult(new ResponseEntity<>(ResultUtil.data(queryResult), HttpStatus.OK)); + break; + } + i ++; + } + } catch (Exception e) { + log.error("获取登录状态异常,",e); + deferredResult.setResult(new ResponseEntity(ResultUtil.error(ResultCode.ERROR), HttpStatus.OK)); + } + }, Executors.newCachedThreadPool()); + return deferredResult; + } + + @ApiOperation(value = "app扫码") + @PostMapping(value = "/app_scanner", produces = "application/json;charset=UTF-8") + public ResultMessage appScanner(String token) { + return ResultUtil.data(memberService.appScanner(token)); + } + + + @ApiOperation(value = "app扫码-登录确认:同意/拒绝") + @ApiImplicitParams({ + @ApiImplicitParam(name = "token", value = "sessionToken", required = true, paramType = "query"), + @ApiImplicitParam(name = "code", value = "操作:0拒绝登录,1同意登录", required = true, paramType = "query") + }) + @PostMapping(value = "/app_confirm", produces = "application/json;charset=UTF-8") + public ResultMessage appSConfirm(String token,Integer code) { + boolean flag = memberService.appSConfirm(token,code); + return flag ? ResultUtil.success():ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "登录接口") @ApiImplicitParams({ @ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query"), diff --git a/framework/src/main/java/cn/lili/cache/CachePrefix.java b/framework/src/main/java/cn/lili/cache/CachePrefix.java index 52592ba6..460fe7bf 100644 --- a/framework/src/main/java/cn/lili/cache/CachePrefix.java +++ b/framework/src/main/java/cn/lili/cache/CachePrefix.java @@ -487,7 +487,16 @@ public enum CachePrefix { /** * 敏感词 */ - SENSITIVE; + SENSITIVE, + + /** + * 扫码登录 + * @param str + * @return + */ + QR_CODE_LOGIN_SESSION + + ; public static String removePrefix(String str) { diff --git a/framework/src/main/java/cn/lili/modules/member/entity/enums/QRCodeLoginSessionStatusEnum.java b/framework/src/main/java/cn/lili/modules/member/entity/enums/QRCodeLoginSessionStatusEnum.java new file mode 100644 index 00000000..7a176f72 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/enums/QRCodeLoginSessionStatusEnum.java @@ -0,0 +1,46 @@ +package cn.lili.modules.member.entity.enums; + + +import lombok.Getter; + +@Getter +public enum QRCodeLoginSessionStatusEnum { + + /** + * 二维码创建完毕,等待app端扫码 + */ + WAIT_SCANNING(0,"等待扫码"), + + /** + * app端已经扫码,等待确认同意登录 + */ + SCANNING(1,"已经扫码"), + + /** + * 用户在app端点击了同意登录 + */ + VERIFIED(2,"确认登录"), + + /** + * 用户在app端点击了取消登录 + */ + CANCELED(3,"取消登录"), + + /** + * 二维码不存在/或者已经过期 + */ + NO_EXIST(4,"二维码已过期") + + ; + + + private Integer code; + + private String desc; + + + QRCodeLoginSessionStatusEnum(Integer code,String desc){ + this.code = code; + this.desc = desc; + } +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/QRCodeLoginSessionVo.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/QRCodeLoginSessionVo.java new file mode 100644 index 00000000..3fb89555 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/QRCodeLoginSessionVo.java @@ -0,0 +1,21 @@ +package cn.lili.modules.member.entity.vo; + +import cn.lili.modules.member.entity.enums.QRCodeLoginSessionStatusEnum; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class QRCodeLoginSessionVo implements Serializable { + + + private static final long serialVersionUID = 8793639296995408322L; + + private String token; + + private Integer status; + + private long duration; + + private long userId; +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/QRLoginResultVo.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/QRLoginResultVo.java new file mode 100644 index 00000000..aaa25a4a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/QRLoginResultVo.java @@ -0,0 +1,12 @@ +package cn.lili.modules.member.entity.vo; + +import cn.lili.common.security.token.Token; +import lombok.Data; + +@Data +public class QRLoginResultVo { + + private Token token; + + private int status; +} diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberService.java index 6824b7cd..25b82f3b 100644 --- a/framework/src/main/java/cn/lili/modules/member/service/MemberService.java +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberService.java @@ -11,6 +11,8 @@ import cn.lili.modules.member.entity.dto.MemberAddDTO; import cn.lili.modules.member.entity.dto.MemberEditDTO; import cn.lili.modules.member.entity.vo.MemberSearchVO; import cn.lili.modules.member.entity.vo.MemberVO; +import cn.lili.modules.member.entity.vo.QRLoginResultVo; +import cn.lili.modules.member.entity.vo.QRCodeLoginSessionVo; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; @@ -272,4 +274,12 @@ public interface MemberService extends IService { * @return 用户VO */ MemberVO getMember(String id); + + QRCodeLoginSessionVo createPcSession(); + + Object appScanner(String token); + + boolean appSConfirm(String token, Integer code); + + QRLoginResultVo loginWithSession(String token); } \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java index 86031a0c..f746b541 100644 --- a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java @@ -26,8 +26,11 @@ import cn.lili.modules.member.aop.annotation.PointLogPoint; import cn.lili.modules.member.entity.dos.Member; import cn.lili.modules.member.entity.dto.*; import cn.lili.modules.member.entity.enums.PointTypeEnum; +import cn.lili.modules.member.entity.enums.QRCodeLoginSessionStatusEnum; import cn.lili.modules.member.entity.vo.MemberSearchVO; import cn.lili.modules.member.entity.vo.MemberVO; +import cn.lili.modules.member.entity.vo.QRLoginResultVo; +import cn.lili.modules.member.entity.vo.QRCodeLoginSessionVo; import cn.lili.modules.member.mapper.MemberMapper; import cn.lili.modules.member.service.MemberService; import cn.lili.modules.member.token.MemberTokenGenerate; @@ -49,10 +52,8 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; +import java.util.concurrent.TimeUnit; /** * 会员接口业务层实现 @@ -668,6 +669,74 @@ public class MemberServiceImpl extends ServiceImpl impleme return new MemberVO(this.getById(id)); } + @Override + public QRCodeLoginSessionVo createPcSession() { + QRCodeLoginSessionVo session = new QRCodeLoginSessionVo(); + session.setStatus(QRCodeLoginSessionStatusEnum.WAIT_SCANNING.getCode()); + //过期时间,10s + Long duration= 10 * 1000L; + session.setDuration(duration); + String token = CachePrefix.QR_CODE_LOGIN_SESSION.name()+SnowFlake.getIdStr(); + session.setToken(token); + cache.put(token,session,duration, TimeUnit.MILLISECONDS); + return session; + } + + @Override + public Object appScanner(String token) { + QRCodeLoginSessionVo session = (QRCodeLoginSessionVo) cache.get(token); + if(session == null){ + return QRCodeLoginSessionStatusEnum.NO_EXIST.getCode(); + } + session.setStatus(QRCodeLoginSessionStatusEnum.SCANNING.getCode()); + cache.put(token,session,session.getDuration(), TimeUnit.MILLISECONDS); + return QRCodeLoginSessionStatusEnum.SCANNING.getCode(); + } + + @Override + public boolean appSConfirm(String token, Integer code) { + QRCodeLoginSessionVo session = (QRCodeLoginSessionVo) cache.get(token); + if(session == null){ + return false; + } + if(code==1){ + //同意 + session.setStatus(QRCodeLoginSessionStatusEnum.VERIFIED.getCode()); + AuthUser currentUser = Objects.requireNonNull(UserContext.getCurrentUser()); + session.setUserId(Long.valueOf(currentUser.getId())); + }else{ + //拒绝 + session.setStatus(QRCodeLoginSessionStatusEnum.CANCELED.getCode()); + } + cache.put(token,session,session.getDuration(), TimeUnit.MILLISECONDS); + return true; + } + + @Override + public QRLoginResultVo loginWithSession(String sessionToken) { + QRLoginResultVo result = new QRLoginResultVo(); + result.setStatus(QRCodeLoginSessionStatusEnum.NO_EXIST.getCode()); + QRCodeLoginSessionVo session = (QRCodeLoginSessionVo) cache.get(sessionToken); + if(session == null){ + return result; + } + result.setStatus(session.getStatus()); + if(QRCodeLoginSessionStatusEnum.VERIFIED.getCode().equals(session.getStatus())){ + //生成token + Member member = this.getById(session.getUserId()); + if(member==null){ + throw new ServiceException(ResultCode.USER_NOT_EXIST); + }else{ + //生成token + Token token = memberTokenGenerate.createToken(member, false); + result.setToken(token); + cache.vagueDel(sessionToken); + } + + } + return result; + } + /** * 检测会员 * diff --git a/framework/src/main/java/cn/lili/modules/member/token/MemberTokenGenerate.java b/framework/src/main/java/cn/lili/modules/member/token/MemberTokenGenerate.java index b83ac909..7b70b5fb 100644 --- a/framework/src/main/java/cn/lili/modules/member/token/MemberTokenGenerate.java +++ b/framework/src/main/java/cn/lili/modules/member/token/MemberTokenGenerate.java @@ -37,17 +37,18 @@ public class MemberTokenGenerate extends AbstractTokenGenerate { @Override public Token createToken(Member member, Boolean longTerm) { - //获取客户端类型 - String clientType = ThreadContextHolder.getHttpRequest().getHeader("clientType"); + ClientTypeEnum clientTypeEnum; try { + //获取客户端类型 + String clientType = ThreadContextHolder.getHttpRequest().getHeader("clientType"); //如果客户端为空,则缺省值为PC,pc第三方登录时不会传递此参数 if (clientType == null) { clientTypeEnum = ClientTypeEnum.PC; } else { clientTypeEnum = ClientTypeEnum.valueOf(clientType); } - } catch (IllegalArgumentException e) { + } catch (Exception e) { clientTypeEnum = ClientTypeEnum.UNKNOWN; } //记录最后登录时间,客户端类型