Browse Source

用户模块接口开发

Bob 2 năm trước cách đây
mục cha
commit
be4f07a6f4

+ 5 - 0
backgroud-gateway/pom.xml

@@ -12,6 +12,11 @@
     <artifactId>backgroud-gateway</artifactId>
 
     <dependencies>
+        <dependency>
+            <groupId>cn.kdan.compdf</groupId>
+            <artifactId>background-redis</artifactId>
+            <version>0.0.1</version>
+        </dependency>
         <dependency>
             <groupId>cn.kdan.compdf</groupId>
             <artifactId>background-common</artifactId>

+ 23 - 6
backgroud-gateway/src/main/java/cn/kdan/compdf/filter/AuthGlobalFilter.java

@@ -2,6 +2,7 @@ package cn.kdan.compdf.filter;
 
 import cn.kdan.compdf.config.JwtConfig;
 import cn.kdan.compdf.dto.LoginUserDTO;
+import cn.kdan.compdf.enums.AuthEnum;
 import cn.kdan.compdf.enums.ResponseEnum;
 import cn.kdan.compdf.exception.BusinessException;
 import com.alibaba.fastjson.JSON;
@@ -11,6 +12,7 @@ import org.springframework.cloud.gateway.filter.GatewayFilterChain;
 import org.springframework.cloud.gateway.filter.GlobalFilter;
 import org.springframework.core.Ordered;
 import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
@@ -21,6 +23,7 @@ import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
 
 import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
 /**
@@ -31,18 +34,18 @@ import java.util.function.Consumer;
 public class AuthGlobalFilter implements GlobalFilter, Ordered {
 
     @Autowired
-    private JwtConfig jwtConfig;
+    private StringRedisTemplate redisTemplate;
 
     @Override
     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
         // 1、todo 判断放行请求地址(登录接口)
         String requestPath = exchange.getRequest().getPath().toString();
-        if (requestPath.equals("登录接口")) {
+        if (requestPath.equals("/user-api/v1/user/login")) {
             return chain.filter(exchange);
         }
 
         // 2、获取请求头jwt
-        String authorization = exchange.getRequest().getHeaders().getFirst("Authorization");
+        String authorization = exchange.getRequest().getHeaders().getFirst(AuthEnum.AUTHORIZATION);
         // 没有jwt
         if (StringUtils.isEmpty(authorization)) {
             ServerHttpResponse response = exchange.getResponse();
@@ -56,10 +59,24 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
 
         // 3、解析
         try {
-            LoginUserDTO loginUserDTO = jwtConfig.checkJwt(authorization);
-            // redie验证token
+            LoginUserDTO loginUserDTO = JwtConfig.checkJwt(authorization);
+            // redie验证token是否过期
+            Boolean flag = redisTemplate.hasKey(AuthEnum.USER_TOKEN_REDIS + loginUserDTO.getUsername());
+            if (null == flag || !flag) {
+                ServerHttpResponse response = exchange.getResponse();
+                response.setStatusCode(HttpStatus.OK);
+                // 改变响应的类型
+                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
+                String fastResult = JSON.toJSONString(new BusinessException(ResponseEnum.TOKEN_TIMEOUT));
+                DataBuffer dataBuffer = response.bufferFactory().allocateBuffer().write(fastResult.getBytes(StandardCharsets.UTF_8));
+                return response.writeWith(Mono.just(dataBuffer));
+            }
+
             Consumer<HttpHeaders> httpHeaders = httpHeader -> {
-                httpHeader.set("teanatId", loginUserDTO.getTeanatId());
+                httpHeader.set(AuthEnum.USER_ID_HEADER, loginUserDTO.getId().toString());
+                httpHeader.set(AuthEnum.USER_TEANATID, loginUserDTO.getTeanatId().toString());
+                httpHeader.set(AuthEnum.USER_NAME_HEADER, loginUserDTO.getUsername());
+//                httpHeader.set(AuthEnum.AUTHORIZATION, authorization);
             };
             ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().headers(httpHeaders).build();
             exchange.mutate().request(serverHttpRequest).build();

+ 24 - 9
backgroud-gateway/src/main/resources/application.yml

@@ -26,15 +26,30 @@ spring:
         locator:
           enabled: true
       routes:
-        - id: gateway  # 只要唯一标识就行
+        - id: gateway
           uri: lb://background-user
           order: 1
-          predicates: # 满足断言中的条件才路由
+          predicates:
             - Path=/user-api/**
-
-config:
-  jwt:
-    # 同这个秘钥生成jwt,只能用这个秘钥解开
-    secret: wwwcompdfkitsaascom
-    # 令牌生存时间(分钟)
-    expire: 60
+        - id: test
+          uri: https://www.baidu.com/
+          order: 1
+          predicates:
+            - Path=/test-api/**
+  redis:
+    host: ${REDIS_HOST:47.93.102.223}
+    port: ${REDIS_PORT:16379}
+    password: ${REDIS_PASSWORD:1qazZAQ!2}
+    database: ${REDIS_DATABASE:1}
+    lettuce:
+      pool:
+        # 连接池中的最小空闲连接
+        min-idle: 1
+        # 连接池中的最大空闲连接
+        max-idle: 6
+        # 连接池最大连接数(使用负值表示没有限制,不要配置过大,否则可能会影响redis的性能)
+        max-active: 10
+        # 连接池最大阻塞等待时间(使用负值表示没有限制);单位毫秒
+        max-wait: 1000
+      # 关闭超时时间;单位毫秒
+      shutdown-timeout: 200

+ 23 - 13
background-common/src/main/java/cn/kdan/compdf/config/JwtConfig.java

@@ -1,7 +1,7 @@
 package cn.kdan.compdf.config;
 
 import cn.kdan.compdf.dto.LoginUserDTO;
-import org.springframework.beans.factory.annotation.Value;
+import cn.kdan.compdf.enums.AuthEnum;
 import org.springframework.stereotype.Component;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.Jws;
@@ -18,27 +18,23 @@ import java.util.Date;
 @Component
 public class JwtConfig {
 
-    @Value("${config.jwt.secret}")
-    private String secret;
-    @Value("${config.jwt.expire}")  // 600
-    private long expire;
-
     /**
      * 加密
      */
-    public  String generateJwt(LoginUserDTO user) {
+    public static String generateJwt(LoginUserDTO user) {
 
         // 加密
-        byte[] keyBytes = secret.getBytes();
+        byte[] keyBytes = AuthEnum.secret.getBytes();
         // 获得密钥对象
         SecretKey key = Keys.hmacShaKeyFor(keyBytes);
 
         String token = Jwts.builder()
                 .setHeaderParam("typ", "JWT") //令牌类型
                 .setIssuedAt(new Date()) //签发时间
-                .setExpiration(new Date(System.currentTimeMillis() + expire * 1000 * 60)) //过期时间
+                .setExpiration(new Date(System.currentTimeMillis() + AuthEnum.expire * 1000 * 60)) //过期时间
                 .claim("id",user.getId())
                 .claim("username", user.getUsername())
+                .claim("fullName", user.getFullName())
                 .claim("teanatId", user.getTeanatId())
                 .signWith(key, SignatureAlgorithm.HS256)
                 .compact();
@@ -46,24 +42,38 @@ public class JwtConfig {
     }
 
     /**
-     * 解析jwt
+     * 解析jwt,一定要捕获异常
      * @param jwtToken
      * @return
      */
-    public LoginUserDTO checkJwt(String jwtToken) {
+    public static LoginUserDTO checkJwt(String jwtToken) {
 
-        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(this.secret.getBytes()).parseClaimsJws(jwtToken);
+        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(AuthEnum.secret.getBytes()).parseClaimsJws(jwtToken);
         // map
         Claims claims = claimsJws.getBody();
         Long id = claims.get("id",Long.class);
         String username = claims.get("username",String.class);
-        String teanatId = claims.get("teanatId",String.class);
+        String fullName = claims.get("fullName",String.class);
+        Long teanatId = claims.get("teanatId",Long.class);
 
         return LoginUserDTO.builder()
                 .id(id)
                 .username(username)
                 .teanatId(teanatId)
+                .fullName(fullName)
                 .build();
     }
 
+/*    public static void main(String[] args) {
+//        String s = generateJwt(LoginUserDTO.builder().id(123L).username("kdan").teanatId("1").fullName("kk").build());
+//        System.out.println(s);
+        try {
+            String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NjU5NzExMjYsImV4cCI6MTY2NTk3NDcyNiwiaWQiOjEyMywidXNlcm5hbWUiOiJrZGFuIiwidGVhbmF0SWQiOiIxIn0.LzPDSixl0RBqYCD60K3FXa8RBEFm9gbYboB6RqINGvM";
+            LoginUserDTO loginUserDTO = checkJwt(token);
+            System.out.println(loginUserDTO);
+        } catch (Exception e) {
+            throw new BusinessException("登录过期");
+        }
+    }*/
+
 }

+ 1 - 1
background-common/src/main/java/cn/kdan/compdf/dto/LoginUserDTO.java

@@ -28,5 +28,5 @@ public class LoginUserDTO {
     /**
      * 企业id
      */
-    private String teanatId;
+    private Long teanatId;
 }

+ 43 - 0
background-common/src/main/java/cn/kdan/compdf/enums/AuthEnum.java

@@ -0,0 +1,43 @@
+package cn.kdan.compdf.enums;
+
+/**
+ * @author Bob 2022-10-17
+ **/
+public interface AuthEnum {
+
+    /**
+     * 秘钥
+     */
+    String secret = "wwwcompdfkitsaascom-wwwcompdfkitsaascom";
+
+    /**
+     * 令牌生存时间(分钟)
+     */
+    long expire = 60;
+
+    /**
+     * Authorization
+     */
+    String AUTHORIZATION = "Authorization";
+
+    /**
+     * 用户id
+     */
+    String USER_ID_HEADER = "x-user-id-header";
+
+    /**
+     * 用户名
+     */
+    String USER_NAME_HEADER = "x-user-name-header";
+
+    /**
+     * 租户id
+     */
+    String USER_TEANATID = "x-user-teanatId";
+
+    /**
+     * token redis存放标识
+     */
+    String USER_TOKEN_REDIS = "user-token-redis:";
+
+}

+ 41 - 0
background-common/src/main/java/cn/kdan/compdf/utils/TokenUtil.java

@@ -0,0 +1,41 @@
+package cn.kdan.compdf.utils;
+
+import cn.kdan.compdf.dto.LoginUserDTO;
+import cn.kdan.compdf.enums.AuthEnum;
+import cn.kdan.compdf.exception.BusinessException;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author Bob 2022-10-17
+ **/
+public class TokenUtil {
+
+    public static LoginUserDTO getRequestHeader() {
+        ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
+        HttpServletRequest request = attr.getRequest();
+
+        String userId = request.getHeader(AuthEnum.USER_ID_HEADER);
+        if (StringUtils.isEmpty(userId)) {
+            throw new BusinessException("非法操作");
+        }
+        String userName = request.getHeader(AuthEnum.USER_NAME_HEADER);
+        if (StringUtils.isEmpty(userName)) {
+            throw new BusinessException("非法操作");
+        }
+        Long teanatId = Long.valueOf(request.getHeader(AuthEnum.USER_TEANATID));
+//        if (null == teanatId) {
+//            throw new BusinessException("非法操作");
+//        }
+
+        return LoginUserDTO.builder()
+                .id(Long.valueOf(userId))
+                .username(userName)
+                .teanatId(teanatId)
+                .build();
+    }
+
+}

+ 14 - 36
background-user/src/main/java/cn/kdan/compdf/controller/v1/BackgroundUserController.java

@@ -1,16 +1,17 @@
 package cn.kdan.compdf.controller.v1;
 
 import cn.kdan.compdf.base.R;
-import cn.kdan.compdf.entity.BackgroundUser;
+import cn.kdan.compdf.dto.ChangePasswordDTO;
+import cn.kdan.compdf.dto.LoginDTO;
 import cn.kdan.compdf.service.BackgroundUserService;
-import com.baomidou.mybatisplus.core.metadata.IPage;
+import cn.kdan.compdf.vo.LoginVO;
 import org.springframework.web.bind.annotation.*;
 
 /**
  * @author comPDF-Kit WPH 2022-10-09
  */
 @RestController
-@RequestMapping("/v1/background-user")
+@RequestMapping("/v1/user")
 public class BackgroundUserController {
 
     private final BackgroundUserService backgroundUserService;
@@ -20,46 +21,23 @@ public class BackgroundUserController {
     }
 
     /**
-     * 获取列表
+     * 用户登录
      */
-    @GetMapping("/list")
-    public R<IPage<BackgroundUser>> list(BackgroundUser query) {
-        IPage<BackgroundUser> page = backgroundUserService.page(query);
-        return R.ok(page);
+    @PostMapping("/login")
+    public R<LoginVO> login(@RequestBody LoginDTO loginDTO) {
+        return backgroundUserService.login(loginDTO);
     }
 
-    /**
-     * 根据id获取
-     */
-    @GetMapping("/{id}")
-    public R<BackgroundUser> info(@PathVariable("id") Long id) {
-        return R.ok(backgroundUserService.getById(id));
-    }
-
-    /**
-     * 保存
-     */
-    @PostMapping
-    public R<Void> save(@RequestBody BackgroundUser backgroundUser) {
-        backgroundUserService.save(backgroundUser);
+    @PostMapping("/logout")
+    public R logout() {
+        backgroundUserService.logout();
         return R.ok();
     }
 
-    /**
-     * 修改
-     */
-    @PutMapping
-    public R<Void> update(@RequestBody BackgroundUser backgroundUser) {
-        backgroundUserService.updateById(backgroundUser);
+    @PostMapping("/changePassword")
+    public R changePassword(@RequestBody ChangePasswordDTO changePasswordDTO) {
+        backgroundUserService.changePassword(changePasswordDTO);
         return R.ok();
     }
 
-    /**
-     * 删除
-     */
-    @DeleteMapping("/{id}")
-    public R<Void> remove(@PathVariable("id") Long id) {
-        backgroundUserService.removeById(id);
-        return R.ok();
-    }
 }

+ 24 - 0
background-user/src/main/java/cn/kdan/compdf/dto/ChangePasswordDTO.java

@@ -0,0 +1,24 @@
+package cn.kdan.compdf.dto;
+
+import lombok.Data;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Bob 2022-10-17
+ */
+@Data
+public class ChangePasswordDTO {
+
+    /**
+     * 原密码
+     */
+    @NotNull("原密码不能为空")
+    private String oldPassword;
+
+    /**
+     * 新密码
+     */
+    @NotNull("新密码不能为空")
+    private String newPassword;
+
+}

+ 24 - 0
background-user/src/main/java/cn/kdan/compdf/dto/LoginDTO.java

@@ -0,0 +1,24 @@
+package cn.kdan.compdf.dto;
+
+import lombok.Data;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Bob 2022-10-17
+ */
+@Data
+public class LoginDTO {
+
+    /**
+     * 用户名(登录名:邮箱)
+     */
+    @NotNull("用户名不能为空")
+    private String username;
+
+    /**
+     * 密码
+     */
+    @NotNull("密码不能为空")
+    private String password;
+
+}

+ 4 - 0
background-user/src/main/java/cn/kdan/compdf/entity/BackgroundUser.java

@@ -21,6 +21,10 @@ public class BackgroundUser extends BaseEntity{
      */
     @TableId(type = IdType.AUTO)
     private Long id;
+    /**
+     * 租户id
+     */
+    private Long tenantId;
     /**
      * 用户名(公司名称)
      */

+ 10 - 0
background-user/src/main/java/cn/kdan/compdf/service/BackgroundUserService.java

@@ -1,6 +1,10 @@
 package cn.kdan.compdf.service;
 
+import cn.kdan.compdf.base.R;
+import cn.kdan.compdf.dto.ChangePasswordDTO;
+import cn.kdan.compdf.dto.LoginDTO;
 import cn.kdan.compdf.entity.BackgroundUser;
+import cn.kdan.compdf.vo.LoginVO;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 
@@ -16,4 +20,10 @@ public interface BackgroundUserService extends IService<BackgroundUser> {
      */
     IPage<BackgroundUser> page(BackgroundUser query);
 
+    R<LoginVO> login(LoginDTO loginDTO);
+
+    void logout();
+
+    void changePassword(ChangePasswordDTO changePasswordDTO);
+
 }

+ 76 - 0
background-user/src/main/java/cn/kdan/compdf/service/impl/BackgroundUserServiceImpl.java

@@ -1,23 +1,99 @@
 package cn.kdan.compdf.service.impl;
 
+import cn.hutool.core.date.DateUtil;
+import cn.kdan.compdf.base.R;
+import cn.kdan.compdf.config.JwtConfig;
+import cn.kdan.compdf.dto.ChangePasswordDTO;
+import cn.kdan.compdf.dto.LoginDTO;
+import cn.kdan.compdf.dto.LoginUserDTO;
 import cn.kdan.compdf.entity.BackgroundUser;
+import cn.kdan.compdf.enums.AuthEnum;
+import cn.kdan.compdf.exception.BusinessException;
 import cn.kdan.compdf.mapper.BackgroundUserMapper;
 import cn.kdan.compdf.service.BackgroundUserService;
+import cn.kdan.compdf.utils.MD5Util;
+import cn.kdan.compdf.utils.TokenUtil;
+import cn.kdan.compdf.vo.LoginVO;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
 
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
 /**
  * @author comPDF-Kit WPH 2022-10-09
  */
 @Service
 public class BackgroundUserServiceImpl extends ServiceImpl<BackgroundUserMapper, BackgroundUser> implements BackgroundUserService {
 
+    @Autowired
+    private StringRedisTemplate redisTemplate;
+
     @Override
     public IPage<BackgroundUser> page(BackgroundUser query) {
         return this.page(new Page<>(query.getPage(), query.getSize()), Wrappers.query(query));
     }
 
+    @Override
+    public R<LoginVO> login(LoginDTO loginDTO) {
+        BackgroundUser backgroundUser = this.baseMapper.selectOne(new LambdaQueryWrapper<BackgroundUser>().eq(BackgroundUser::getUsername, loginDTO.getUsername()));
+        if (null == backgroundUser) {
+            throw new BusinessException("账号不存在");
+        }
+        //转换成MD5密码
+        String md5Password = MD5Util.MD5Encode(loginDTO.getPassword());
+        if (!md5Password.equals(backgroundUser.getPassword())) {
+            throw new BusinessException("密码错误");
+        }
+        String token = JwtConfig.generateJwt(LoginUserDTO
+                .builder()
+                .id(backgroundUser.getId())
+                .username(backgroundUser.getUsername())
+                .fullName(backgroundUser.getFullName())
+                .teanatId(backgroundUser.getTenantId())
+                .build());
+
+        redisTemplate.opsForValue().set(AuthEnum.USER_TOKEN_REDIS + backgroundUser.getUsername(), token,2, TimeUnit.HOURS);
+
+        //更新登录用户的登陆时间
+        if (backgroundUser.getFirstLoginTime() == null) {
+            backgroundUser.setFirstLoginTime(new Date());
+        }
+        backgroundUser.setLastLoginTime(new Date());
+        baseMapper.updateById(backgroundUser);
+
+        LoginVO loginVO = new LoginVO();
+        loginVO.setId(backgroundUser.getId());
+        loginVO.setUsername(backgroundUser.getUsername());
+        loginVO.setToken(token);
+        return R.ok(loginVO);
+    }
+
+    @Override
+    public void logout() {
+        redisTemplate.delete(AuthEnum.USER_TOKEN_REDIS + TokenUtil.getRequestHeader().getUsername());
+    }
+
+    @Override
+    public void changePassword(ChangePasswordDTO changePasswordDTO) {
+        //
+        LoginUserDTO requestHeader = TokenUtil.getRequestHeader();
+        BackgroundUser backgroundUser = this.baseMapper.selectOne(new LambdaQueryWrapper<BackgroundUser>().eq(BackgroundUser::getId, requestHeader.getId()));
+        //转换成MD5密码
+        String oldPassword = MD5Util.MD5Encode(changePasswordDTO.getOldPassword());
+        if (!oldPassword.equals(backgroundUser.getPassword())) {
+            throw new BusinessException("原密码错误");
+        }
+        //转换成MD5密码
+        String newPassword = MD5Util.MD5Encode(changePasswordDTO.getNewPassword());
+        backgroundUser.setPassword(newPassword);
+        baseMapper.updateById(backgroundUser);
+        redisTemplate.delete(AuthEnum.USER_TOKEN_REDIS + backgroundUser.getUsername());
+    }
 }

+ 27 - 0
background-user/src/main/java/cn/kdan/compdf/vo/LoginVO.java

@@ -0,0 +1,27 @@
+package cn.kdan.compdf.vo;
+
+import lombok.Data;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Bob 2022-10-17
+ */
+@Data
+public class LoginVO {
+
+    /**
+     * 用户id
+     */
+    private Long id;
+
+    /**
+     * 用户名(登录名:邮箱)
+     */
+    private String username;
+
+    /**
+     * token
+     */
+    private String token;
+
+}

+ 0 - 7
background-user/src/main/resources/application.yml

@@ -51,10 +51,3 @@ mybatis-plus:
   type-aliases-package: cn.kdan.compdf
   configuration:
     map-underscore-to-camel-case: true
-
-config:
-  jwt:
-    # 同这个秘钥生成jwt,只能用这个秘钥解开
-    secret: wwwcompdfkitsaascom
-    # 令牌生存时间(分钟)
-    expire: 60

+ 1 - 0
background-user/src/main/resources/mapper/BackgroundUserMapper.xml

@@ -6,6 +6,7 @@
     <!-- 可根据自己的需求,是否要使用 -->
     <resultMap id="BackgroundUserResultMap" type="cn.kdan.compdf.entity.BackgroundUser">
         <result property="id" column="id" />
+        <result property="tenantId" column="tenant_id" />
         <result property="fullName" column="full_name" />
         <result property="username" column="username" />
         <result property="password" column="password" />

+ 1 - 1
pom.xml

@@ -33,7 +33,7 @@
 		<io.version>2.11.0</io.version>
 		<fastjson.version>1.2.60</fastjson.version>
 		<jjwt.version>0.10.7</jjwt.version>
-		<gateway.version>2.1.1.RELEASE</gateway.version>
+		<gateway.version>2.2.6.RELEASE</gateway.version>
 	</properties>
 
 </project>