浏览代码

微校用户登录开发完成

wangzhengliang 3 年之前
父节点
当前提交
6f9f80feb3

+ 7 - 7
src/main/java/com/chuanghai/ihotel/aop/LoginCheckAspect.java

@@ -4,7 +4,7 @@ package com.chuanghai.ihotel.aop;
 import com.chuanghai.ihotel.common.exception.BizCodeEnume;
 import com.chuanghai.ihotel.common.exception.RRException;
 import com.chuanghai.ihotel.util.JWTUtil;
-import com.chuanghai.ihotel.vo.LoginUserVO;
+import com.chuanghai.ihotel.dto.LoginUserDTO;
 import io.jsonwebtoken.Claims;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
@@ -29,7 +29,7 @@ import javax.servlet.http.HttpServletRequest;
 @Order(0)
 public class LoginCheckAspect {
 
-    public static ThreadLocal<LoginUserVO> threadLocal = new ThreadLocal<>();
+    public static ThreadLocal<LoginUserDTO> threadLocal = new ThreadLocal<>();
 
     /**
      * 管理员登录拦截
@@ -38,14 +38,14 @@ public class LoginCheckAspect {
      * @throws Throwable
      */
     @Around("@annotation(com.chuanghai.ihotel.anno.AdminLoginCheck)")
-    public Object userLoginCheck(ProceedingJoinPoint point) throws Throwable {
+    public Object adminLoginCheck(ProceedingJoinPoint point) throws Throwable {
         Claims claims = validateToken("admin_token");
         Long adminId = Long.valueOf((String) claims.get("admin_id"));
         if (adminId == null) {
             throw new RRException(BizCodeEnume.TOKEN_INVALID);
         }
 
-        LoginUserVO loginUserVO = LoginUserVO.builder().adminId(adminId).build();
+        LoginUserDTO loginUserVO = LoginUserDTO.builder().adminId(adminId).build();
 
         Object proceed;
         try {
@@ -59,13 +59,13 @@ public class LoginCheckAspect {
     }
 
     /**
-     * 评分员登录拦截
+     * 普通用户登录拦截
      * @param point
      * @return
      * @throws Throwable
      */
     @Around("@annotation(com.chuanghai.ihotel.anno.UserLoginCheck)")
-    public Object raterLoginCheck(ProceedingJoinPoint point) throws Throwable {
+    public Object userLoginCheck(ProceedingJoinPoint point) throws Throwable {
         // 校验token
         Claims claims = validateToken("user_token");
 
@@ -75,7 +75,7 @@ public class LoginCheckAspect {
         if (!StringUtils.hasText(cardNumber)) {
             throw new RRException(BizCodeEnume.TOKEN_INVALID);
         }
-        LoginUserVO loginUserVO = LoginUserVO.builder().cardNumber(cardNumber).identityType(identityType).build();
+        LoginUserDTO loginUserVO = LoginUserDTO.builder().cardNumber(cardNumber).identityType(identityType).build();
         Object proceed;
         try {
             threadLocal.set(loginUserVO);

+ 20 - 0
src/main/java/com/chuanghai/ihotel/config/WeixiaoConfig.java

@@ -0,0 +1,20 @@
+package com.chuanghai.ihotel.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Author: codingliang
+ * @Description: 微校相关配置
+ * @Date: 2021-06-21 15:26
+ * @Version: V1.0
+ **/
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "weixiao")
+public class WeixiaoConfig {
+
+    private String appKey;
+    private String appSecret;
+}

+ 2 - 2
src/main/java/com/chuanghai/ihotel/controller/HotelOrderController.java

@@ -18,7 +18,7 @@ import com.chuanghai.ihotel.entity.HotelOrderEntity;
 import com.chuanghai.ihotel.service.HotelOrderService;
 import com.chuanghai.ihotel.util.CommonUtil;
 import com.chuanghai.ihotel.vo.ConfirmOrderVO;
-import com.chuanghai.ihotel.vo.LoginUserVO;
+import com.chuanghai.ihotel.dto.LoginUserDTO;
 import com.chuanghai.ihotel.vo.OrderBillHandleVO;
 import com.chuanghai.ihotel.vo.OrderSubmitTokenVO;
 import com.chuanghai.ihotel.vo.OrderSubmitVO;
@@ -66,7 +66,7 @@ public class HotelOrderController {
     @UserLoginCheck
     @GetMapping("/user/submit/token")
     public CommonResult<OrderSubmitTokenVO> getOrderToken(@RequestHeader("user_token") String userToken){
-        LoginUserVO loginUserVO = LoginCheckAspect.threadLocal.get();
+        LoginUserDTO loginUserVO = LoginCheckAspect.threadLocal.get();
         String cardNumber = loginUserVO.getCardNumber();
         String token = CommonUtil.getStringNumRandom(32);
         String key = String.format(RedisKey.SUBMIT_ORDER_TOKEN_KEY_FOR_TOKEN, cardNumber, token);

+ 53 - 0
src/main/java/com/chuanghai/ihotel/controller/HotelUserController.java

@@ -0,0 +1,53 @@
+package com.chuanghai.ihotel.controller;
+
+import com.chuanghai.ihotel.service.HotelUserService;
+import com.chuanghai.ihotel.vo.LoginUserVO;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 员工用户
+ *
+ * @author codingliang
+ * @email codingliang@gmail.com
+ * @date 2022-07-27 10:02:04
+ */
+@RestController
+@RequestMapping("hotelUser")
+public class HotelUserController {
+
+    @Value("${fontEndUrl}")
+    private String fontEndUrl;
+    @Autowired
+    private HotelUserService hotelUserService;
+
+    /**
+     * 微校授权回调地址
+     *
+     * @apiNote 用户跳转到微校授权链接上,微校服务器接收用户授权后会携带wxcode回调到该接口上,该接口使用wxcode获取到用户的信息。
+     *          如果用户信息获取成功,该接口会以 【fontEndUrl + /?loginUserVO={}】 的形式跳回到前端应用页面;
+     *          如果用户信息获取失败,该接口会以 【fontEndUrl + /error/?errorMsg=获取用户信息失败】 的形式跳回到前端应用页面。
+     *          <strong>注意:</strong>
+     *          fontEndUrl + 路由地址:前端项目地址+页面路由地址,用于前端页面接收用户的信息或错误信息,该地址需要前端人员与后台开发人员协调好。
+     * </p>
+     * @param wxcode 微校code
+     * @param state 透传数据
+     * @return
+     */
+    @GetMapping("weixiaoAuth")
+    public String weixiaoAuth(String wxcode, String state) throws JsonProcessingException {
+        LoginUserVO loginUserVO = hotelUserService.weixiaoAuth(wxcode, state);
+
+        if (loginUserVO != null) {
+            ObjectMapper objectMapper = new ObjectMapper();
+            return "redirect:" + fontEndUrl + "/#/pages/blankIndex/blankIndex/?loginUserVO=" + objectMapper.writeValueAsString(loginUserVO);
+        } else {
+            return "redirect:" + fontEndUrl + "/#/error/?errorMsg=获取用户信息失败";
+        }
+    }
+}

+ 32 - 0
src/main/java/com/chuanghai/ihotel/dto/LoginUserDTO.java

@@ -0,0 +1,32 @@
+package com.chuanghai.ihotel.dto;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @Author: codingliang
+ * @Description: 登录用户
+ * @Date: 2022-07-28 10:24
+ * @Version: V1.0
+ **/
+@Data
+@Builder
+public class LoginUserDTO {
+
+    /**
+     * 用户类型,1管理员、2普通用户
+     */
+    private String userType;
+    /**
+     * 管理员id,userType为1时该字段有值
+     */
+    private Long adminId;
+    /**
+     * 卡号,userType为2时该字段有值
+     */
+    private String cardNumber;
+    /**
+     * 身份,userType为2时该字段有值 0其他、1学生、4教职工、5校友
+     */
+    private String identityType;
+}

+ 9 - 0
src/main/java/com/chuanghai/ihotel/service/HotelUserService.java

@@ -2,6 +2,7 @@ package com.chuanghai.ihotel.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.chuanghai.ihotel.entity.HotelUserEntity;
+import com.chuanghai.ihotel.vo.LoginUserVO;
 
 /**
  * 酒店用户
@@ -13,5 +14,13 @@ import com.chuanghai.ihotel.entity.HotelUserEntity;
 public interface HotelUserService extends IService<HotelUserEntity> {
 
     HotelUserEntity findByCardNumber(String cardNumber);
+
+    /**
+     * 微校授权
+     * @param wxcode
+     * @param state
+     * @return
+     */
+    LoginUserVO weixiaoAuth(String wxcode, String state);
 }
 

+ 4 - 4
src/main/java/com/chuanghai/ihotel/service/impl/HotelOrderServiceImpl.java

@@ -43,7 +43,7 @@ import com.chuanghai.ihotel.service.RoomTypeService;
 import com.chuanghai.ihotel.service.SystemSettingService;
 import com.chuanghai.ihotel.util.CommonUtil;
 import com.chuanghai.ihotel.vo.ConfirmOrderVO;
-import com.chuanghai.ihotel.vo.LoginUserVO;
+import com.chuanghai.ihotel.dto.LoginUserDTO;
 import com.chuanghai.ihotel.vo.OrderBillHandleVO;
 import com.chuanghai.ihotel.vo.OrderSubmitVO;
 import com.chuanghai.ihotel.vo.UserOrderDetailVO;
@@ -132,7 +132,7 @@ public class HotelOrderServiceImpl extends ServiceImpl<HotelOrderDao, HotelOrder
         pageParam.setOrderField("create_time");
         pageParam.setOrder("desc");
 
-        LoginUserVO loginUserVO = LoginCheckAspect.threadLocal.get();
+        LoginUserDTO loginUserVO = LoginCheckAspect.threadLocal.get();
         request.setUserFlag(loginUserVO.getCardNumber());
         request.setDeleteFlag("1"); // 1未删除
 
@@ -242,7 +242,7 @@ public class HotelOrderServiceImpl extends ServiceImpl<HotelOrderDao, HotelOrder
     @Transactional
     @Override
     public OrderSubmitVO submitOrder(SubmitOrderRequest request) {
-        LoginUserVO loginUserVO = LoginCheckAspect.threadLocal.get();
+        LoginUserDTO loginUserVO = LoginCheckAspect.threadLocal.get();
         String cardNumber = loginUserVO.getCardNumber();
 
         // 身份校验
@@ -553,7 +553,7 @@ public class HotelOrderServiceImpl extends ServiceImpl<HotelOrderDao, HotelOrder
             throw new RRException(BizCodeEnume.PARAMETER_ERROR, "订单不存在,无效的orderId");
         }
 
-        LoginUserVO loginUserVO = LoginCheckAspect.threadLocal.get();
+        LoginUserDTO loginUserVO = LoginCheckAspect.threadLocal.get();
         String userFlag = loginUserVO.getCardNumber();
         if (!orderEntity.getUserFlag().equalsIgnoreCase(userFlag)) {
             throw new RRException(BizCodeEnume.PERMISSION_DENIED, "非自己的订单");

+ 91 - 0
src/main/java/com/chuanghai/ihotel/service/impl/HotelUserServiceImpl.java

@@ -2,15 +2,31 @@ package com.chuanghai.ihotel.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.ihotel.config.WeixiaoConfig;
 import com.chuanghai.ihotel.dao.HotelUserDao;
+import com.chuanghai.ihotel.dto.LoginUserDTO;
 import com.chuanghai.ihotel.entity.HotelUserEntity;
 import com.chuanghai.ihotel.service.HotelUserService;
+import com.chuanghai.ihotel.util.JWTUtil;
+import com.chuanghai.ihotel.vo.LoginUserVO;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
 
+import java.util.HashMap;
+import java.util.Map;
 
+@Slf4j
 @Service("hotelUserService")
 public class HotelUserServiceImpl extends ServiceImpl<HotelUserDao, HotelUserEntity> implements HotelUserService {
 
+    @Autowired
+    private WeixiaoConfig weixiaoConfig;
+
     @Override
     public HotelUserEntity findByCardNumber(String cardNumber) {
         QueryWrapper<HotelUserEntity> queryWrapper = new QueryWrapper<>();
@@ -19,4 +35,79 @@ public class HotelUserServiceImpl extends ServiceImpl<HotelUserDao, HotelUserEnt
         queryWrapper.last("limit 1");
         return this.getOne(queryWrapper);
     }
+
+    @Override
+    public LoginUserVO weixiaoAuth(String wxcode, String state) {
+        String tokenUrl = "https://open.wecard.qq.com/connect/oauth2/token";
+        Map<String, String> tokenParams = new HashMap<>();
+        tokenParams.put("wxcode", wxcode);
+        tokenParams.put("app_key", weixiaoConfig.getAppKey());
+        tokenParams.put("app_secret", weixiaoConfig.getAppSecret());
+        tokenParams.put("grant_type", "authorization_code");
+        tokenParams.put("redirect_uri", state); // 该地址需要与发起授权地址保持一直
+
+        try {
+            // wxcode换取token
+            RestTemplate client = new RestTemplate();
+            ResponseEntity<String> tokenResponse = client.postForEntity(tokenUrl, tokenParams, String.class);
+            String body = tokenResponse.getBody();
+            log.info("微校code换token返回结果【{}】", body);
+
+            ObjectMapper mapper = new ObjectMapper();
+            Map<String, Object> jsonMap = mapper.readValue(body, new TypeReference<Map<String, Object>>() {});
+            String accessToken = (String) jsonMap.get("access_token");
+
+            // token换取用户信息
+            String userInfoUrl = "https://open.wecard.qq.com/connect/oauth/get-user-info";
+            Map<String, String> userInfoParam = new HashMap<>();
+            userInfoParam.put("access_token", accessToken);
+            ResponseEntity<String> userInfoResponse = client.postForEntity(userInfoUrl, userInfoParam, String.class);
+            String userInfoResponseBody = userInfoResponse.getBody();
+            log.info("微校token换用户信息返回结果【{}】", body);
+
+            // 解析用户信息
+            Map<String, Object> userInfoMap = mapper.readValue(userInfoResponseBody, new TypeReference<Map<String, Object>>() {});
+            String cardNumber = userInfoMap.get("card_number").toString();
+            String name = userInfoMap.get("name").toString();
+            String identityType = userInfoMap.get("identity_type").toString();
+
+            // 用户信息生成token返回
+            return generateToken(cardNumber, name, identityType);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * 微校用户信息生成token返回
+     * @param cardNumber
+     * @param name
+     * @param identityType
+     * @return
+     */
+    private LoginUserVO generateToken(String cardNumber, String name, String identityType) {
+        HotelUserEntity hotelUser = findByCardNumber(cardNumber);
+        if (hotelUser == null) {
+            hotelUser = new HotelUserEntity();
+            hotelUser.setName(name);
+            hotelUser.setCardNumber(cardNumber);
+            hotelUser.setIdentityType(identityType);
+            hotelUser.setStatu("1");
+
+            this.save(hotelUser);
+        }
+
+        // 生成token
+        LoginUserDTO dto = LoginUserDTO.builder().cardNumber(cardNumber).userType("2").identityType(identityType).build();
+        String token = JWTUtil.geneJsonWebToken(dto);
+
+        // 返回登录对象
+        return LoginUserVO.builder()
+                .token(token)
+                .tokenTtl(JWTUtil.getExpired())
+                .userType("2")
+                .userName(name)
+                .userIdentity(identityType)
+                .build();
+    }
 }

+ 10 - 3
src/main/java/com/chuanghai/ihotel/util/JWTUtil.java

@@ -1,6 +1,6 @@
 package com.chuanghai.ihotel.util;
 
-import com.chuanghai.ihotel.vo.LoginUserVO;
+import com.chuanghai.ihotel.dto.LoginUserDTO;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
@@ -35,6 +35,13 @@ public class JWTUtil {
      */
     private static final long EXPIRED = 1000 * 60 * 60 * 24 * 7;
 
+    /**
+     * 获取过期时间
+     * @return
+     */
+    public static long getExpired() {
+        return EXPIRED;
+    }
 
     /**
      * 生成token
@@ -42,7 +49,7 @@ public class JWTUtil {
      * @param loginUser
      * @return
      */
-    public static String geneJsonWebToken(LoginUserVO loginUser) {
+    public static String geneJsonWebToken(LoginUserDTO loginUser) {
         if (loginUser == null) {
             throw new NullPointerException("对象为空");
         }
@@ -75,6 +82,6 @@ public class JWTUtil {
     }
 
     public static void main(String[] args) {
-        System.out.println(geneJsonWebToken(LoginUserVO.builder().adminId(1L).cardNumber("123456").identityType("1").build()));
+        System.out.println(geneJsonWebToken(LoginUserDTO.builder().adminId(1L).cardNumber("123456").identityType("1").build()));
     }
 }

+ 14 - 10
src/main/java/com/chuanghai/ihotel/vo/LoginUserVO.java

@@ -5,28 +5,32 @@ import lombok.Data;
 
 /**
  * @Author: codingliang
- * @Description: 登录用户
- * @Date: 2022-07-28 10:24
+ * @Description: 登录用户vo
+ * @Date: 2022-08-04 9:58
  * @Version: V1.0
  **/
-@Data
 @Builder
+@Data
 public class LoginUserVO {
 
     /**
-     * 用户类型,1管理员、2普通用户
+     * 用户姓名
+     */
+    private String userName;
+    /**
+     * 用户类别 1管理员、2普通用户
      */
     private String userType;
     /**
-     * 管理员id,userType为1时该字段有值
+     * 用户身份 只有userType为2时该字段才有值 0其他、1学生、4教职工、5校友
      */
-    private Long adminId;
+    private String userIdentity;
     /**
-     * 卡号,userType为2时该字段有值
+     * 用户token
      */
-    private String cardNumber;
+    private String token;
     /**
-     * 身份,userType为2时该字段有值 0其他、1学生、4教职工、5校友
+     * token过期时间
      */
-    private String identityType;
+    private long tokenTtl;
 }

+ 5 - 0
src/main/resources/application.yml

@@ -95,3 +95,8 @@ door-lock:
   app-secret: 4f444dfc501b44e1a8641a1c1c1ec618
   category-id: d0c248256f8346d2a19afa296562b319
   service-host: https://www.qspms.cn/
+
+# 微校相关配置
+weixiao:
+  app-key: 2DDC9DBF32F28845
+  app-secret: 97F7E3A65AD8A1C500DDD90F3D57EBE0