Procházet zdrojové kódy

1.添加拦截器防止私自访问服务
2.修改登录功能,添加下线提示
3.添加登出功能

songxinlu před 3 roky
rodič
revize
0377f2690d
22 změnil soubory, kde provedl 549 přidání a 2 odebrání
  1. 35 0
      bus-interceptor/pom.xml
  2. 36 0
      bus-interceptor/src/main/java/com/lantone/interceptor/config/HttpInterceptorConfig.java
  3. 45 0
      bus-interceptor/src/main/java/com/lantone/interceptor/interceptor/HttpInterceptor.java
  4. 1 0
      bus-interceptor/src/main/resources/application.properties
  5. 10 0
      common/pom.xml
  6. 4 0
      common/src/main/java/com/lantone/common/config/BaseRedisConfig.java
  7. 12 0
      common/src/main/java/com/lantone/common/constant/AuthConstant.java
  8. 17 0
      common/src/main/java/com/lantone/common/dto/JwtStore.java
  9. 2 0
      common/src/main/java/com/lantone/common/dto/SendToTopicDTO.java
  10. 56 0
      common/src/main/java/com/lantone/common/service/SysTokenService.java
  11. 217 0
      common/src/main/java/com/lantone/common/service/impl/SysTokenServiceImpl.java
  12. 6 0
      gateway-service/src/main/java/com/lantone/authorization/AuthorizationManager.java
  13. 4 0
      gateway-service/src/main/java/com/lantone/config/ResourceServerConfig.java
  14. 33 0
      gateway-service/src/main/java/com/lantone/filter/GateWayFilter.java
  15. 1 0
      gateway-service/src/main/resources/bootstrap.yml
  16. 1 0
      pom.xml
  17. 5 0
      security-center/pom.xml
  18. 1 0
      security-center/src/main/java/com/lantone/security/facade/NoticeManagementFacade.java
  19. 50 1
      security-center/src/main/java/com/lantone/security/facade/UserManagementFacade.java
  20. 5 0
      security-center/src/main/java/com/lantone/security/web/UserManagementController.java
  21. 6 0
      user-auth/src/main/java/com/lantone/UserAuthApplication.java
  22. 2 1
      user-auth/src/main/resources/bootstrap.yml

+ 35 - 0
bus-interceptor/pom.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.lantone</groupId>
+        <artifactId>emrais</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>bus-interceptor</artifactId>
+    <name>bus-interceptor</name>
+    <packaging>jar</packaging>
+
+    <properties>
+        <java.version>1.8</java.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>com.lantone</groupId>
+            <artifactId>common</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 36 - 0
bus-interceptor/src/main/java/com/lantone/interceptor/config/HttpInterceptorConfig.java

@@ -0,0 +1,36 @@
+package com.lantone.interceptor.config;
+
+import com.lantone.interceptor.interceptor.HttpInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.EnvironmentAware;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+/**
+ * @Description:
+ * @Author songxl
+ * @Date 2021/9/26
+ */
+@PropertySource("classpath:application.properties")
+@Configuration
+public class HttpInterceptorConfig implements WebMvcConfigurer, EnvironmentAware {
+    @Autowired
+    private HttpInterceptor httpInterceptor;
+    private Environment env;
+
+    @Override
+    public void setEnvironment(Environment environment) {
+        this.env=environment;
+    }
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(httpInterceptor).addPathPatterns("/**")
+                .excludePathPatterns(env.getProperty("url",List.class));
+
+    }
+}

+ 45 - 0
bus-interceptor/src/main/java/com/lantone/interceptor/interceptor/HttpInterceptor.java

@@ -0,0 +1,45 @@
+package com.lantone.interceptor.interceptor;
+
+import com.lantone.common.constant.AuthConstant;
+import com.lantone.common.service.RedisService;
+import com.lantone.common.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @Description:
+ * @Author songxl
+ * @Date 2021/9/26
+ */
+@Component
+public class HttpInterceptor implements HandlerInterceptor {
+    @Autowired
+    private RedisService redisService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
+        String uuid = httpServletRequest.getHeader(AuthConstant.UUID);
+        if (StringUtil.isNotBlank(uuid)) {
+            if (uuid.equals(redisService.get(AuthConstant.UUID))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
+
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
+
+    }
+}
+

+ 1 - 0
bus-interceptor/src/main/resources/application.properties

@@ -0,0 +1 @@
+url =/userManage/loadByUsername,/redisRefresh,/loginLogHandle

+ 10 - 0
common/pom.xml

@@ -88,6 +88,16 @@
             <groupId>com.nimbusds</groupId>
             <artifactId>nimbus-jose-jwt</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>0.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>3.4.0</version>
+        </dependency>
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-extension</artifactId>

+ 4 - 0
common/src/main/java/com/lantone/common/config/BaseRedisConfig.java

@@ -25,6 +25,10 @@ import java.time.Duration;
  * @time: 2021/1/5 18:27
  */
 public class BaseRedisConfig {
+    @Bean(name = "redisTemplateForToken")
+    public RedisTemplate<String, Object> redisTemplateForToken(RedisConnectionFactory redisConnectionFactory) {
+        return redisTemplate(redisConnectionFactory);
+    }
 
     @Bean
     public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {

+ 12 - 0
common/src/main/java/com/lantone/common/constant/AuthConstant.java

@@ -69,4 +69,16 @@ public interface AuthConstant {
      * 数据权限过滤sql key
      */
     String DATAAUTH_FILTER_SQL = "dataAuthFilter";
+    /**
+     * 网关UUID
+     */
+    String UUID = "UUID";
+    /**
+     * Token
+     */
+    String ASSESS_TOKEN = "token";
+    /**
+     * Token
+     */
+    String REFRESH_TOKEN = "refreshToken";
 }

+ 17 - 0
common/src/main/java/com/lantone/common/dto/JwtStore.java

@@ -0,0 +1,17 @@
+package com.lantone.common.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @Description:
+ * @author: gaodm
+ * @time: 2018/10/29 14:38
+ */
+@Getter
+@Setter
+public class JwtStore {
+    private String accessToken;
+    private String refreshToken;
+    private String ip;
+}

+ 2 - 0
common/src/main/java/com/lantone/common/dto/SendToTopicDTO.java

@@ -13,6 +13,8 @@ public class SendToTopicDTO implements Serializable {
     private static final long serialVersionUID = 1L;
     @ApiModelProperty(value = "信息")
     private String message;
+    @ApiModelProperty(value = "类型")
+    private String type;
     @ApiModelProperty(value = "topic")
     private String topic;
 }

+ 56 - 0
common/src/main/java/com/lantone/common/service/SysTokenService.java

@@ -0,0 +1,56 @@
+package com.lantone.common.service;
+
+import com.lantone.common.dto.JwtStore;
+
+import java.util.List;
+
+/**
+ * @Description: Token验证类
+ * @author: gaodm
+ * @time: 2018/10/29 13:35
+ */
+public interface SysTokenService {
+
+    /**
+     * 创建token
+     *
+     * @param token 用户token
+     * @return
+     */
+    Boolean createToken(JwtStore token);
+
+    /**
+     * 验证token是否有效
+     *
+     * @param token 待验证的token
+     * @param type  1:accessToken,2:refreshToken
+     * @return
+     */
+    Boolean verifyToken(String token, Integer type);
+
+    /**
+     * 删除用户token
+     *
+     * @param userId 用户ID
+     * @return 删除是否成功
+     */
+    Boolean deleteToken(String userId);
+
+    /**
+     * 批量删除用户token
+     *
+     * @param userIds 用户列表
+     * @return 删除是否成功
+     */
+    Boolean deleteBatchToken(List<Long> userIds);
+
+    /**
+     * 获取用户jwt
+     *
+     * @param userId 用户ID
+     * @return jwt信息
+     */
+    JwtStore getToken(String userId);
+
+    String getUserIDByToken(String token);
+}

+ 217 - 0
common/src/main/java/com/lantone/common/service/impl/SysTokenServiceImpl.java

@@ -0,0 +1,217 @@
+package com.lantone.common.service.impl;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.interfaces.Claim;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.lantone.common.dto.JwtStore;
+import com.lantone.common.exception.Asserts;
+import com.lantone.common.service.SysTokenService;
+import com.lantone.common.util.DateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: Token验证类 实现
+ * @author: gaodm
+ * @time: 2018/10/29 13:34
+ */
+@Service
+public class SysTokenServiceImpl implements SysTokenService {
+
+    @Autowired
+    @Qualifier("redisTemplateForToken")
+    RedisTemplate redisForToken;
+
+    private byte[] serializeKey(Object o) {
+        return redisForToken.getKeySerializer().serialize(o);
+    }
+
+    private byte[] serializeValue(Object o) {
+        return redisForToken.getValueSerializer().serialize(o);
+    }
+
+    private Object deserializeValue(byte[] b) {
+        return redisForToken.getValueSerializer().deserialize(b);
+    }
+
+    private byte[] getUserTokenKey(String userId) {
+        String userTokensFormat = "user_tokens_%s";
+        return serializeKey(String.format(userTokensFormat, userId));
+    }
+
+    /**
+     * 创建token
+     *
+     * @param token 用户token
+     * @return
+     */
+    @Override
+    public Boolean createToken(JwtStore token) {
+        DecodedJWT jwt = decodedJWT(token.getRefreshToken());
+        Map<String, Claim> claims = jwt.getClaims();
+        String userId = claims.get("id").asInt().toString();
+        Date expDate = claims.get("exp").asDate();
+        final byte[] redis_key = getUserTokenKey(userId);
+        redisForToken.execute(new RedisCallback<Object>() {
+            @Override
+            public Object doInRedis(RedisConnection connection) throws DataAccessException {
+                //获取旧的
+                byte[] bytes = connection.get(redis_key);
+                //删除旧的
+                if (bytes != null) {
+                    connection.del(bytes);
+                }
+                //设置新的
+                connection.setEx(
+                        redis_key,
+                        (expDate.getTime() - DateUtil.now().getTime()) / 1000,
+                        serializeValue(token)
+                );
+                return true;
+            }
+        });
+        return true;
+    }
+    private DecodedJWT decodedJWT(String token) {
+        try {
+            DecodedJWT jwt = JWT.decode(token);
+            return jwt;
+        } catch (JWTDecodeException var2) {
+            var2.printStackTrace();
+            Asserts.fail("token解析失败!");
+        }
+        return null;
+    }
+
+    /**
+     * 验证token是否有效
+     *
+     * @param token 待验证的token
+     * @param type  1:accessToken,2:refreshToken
+     * @return
+     */
+    @Override
+    public Boolean verifyToken(String token, Integer type) {
+        Boolean res = false;
+        if (null == token) {
+            return false;
+        }
+        DecodedJWT jwt = decodedJWT(token);
+        Map<String, Claim> claims = jwt.getClaims();
+        Claim claim = (Claim)claims.get("id");
+        String userId = claim.asInt().toString();
+        //从redis中取出
+        final byte[] redis_key = getUserTokenKey(userId);
+        JwtStore tokenStore = (JwtStore) redisForToken.execute(new RedisCallback<JwtStore>() {
+            @Override
+            public JwtStore doInRedis(RedisConnection connection) throws DataAccessException {
+                byte[] bytes = connection.get(redis_key);
+                if (bytes == null) {
+                    return null;
+                }
+                return (JwtStore) deserializeValue(bytes);
+            }
+        });
+
+        if (null != tokenStore) {
+            if (type == 1) {
+                if (null != tokenStore.getAccessToken() && tokenStore.getAccessToken().equals(token)) {
+                    res = true;
+                }
+            }
+
+            if (type == 2) {
+                if (null != tokenStore.getRefreshToken() && tokenStore.getRefreshToken().equals(token)) {
+                    res = true;
+                }
+            }
+        }
+
+        return res;
+    }
+
+    /**
+     * 删除用户token
+     *
+     * @param userId 用户ID
+     * @return 删除是否成功
+     */
+    @Override
+    public Boolean deleteToken(String userId) {
+        final byte[] redis_key = getUserTokenKey(userId);
+        Long l = (Long) redisForToken.execute(new RedisCallback<Long>() {
+            @Override
+            public Long doInRedis(RedisConnection connection) throws DataAccessException {
+                return connection.del(redis_key);
+            }
+        });
+        return l > 0;
+    }
+
+    /**
+     * 批量删除用户token
+     *
+     * @param userIds 用户列表
+     * @return 删除是否成功
+     */
+    @Override
+    public Boolean deleteBatchToken(List<Long> userIds) {
+        Long l = (Long) redisForToken.execute(new RedisCallback<Long>() {
+            @Override
+            public Long doInRedis(RedisConnection connection) throws DataAccessException {
+                connection.openPipeline();
+                Long cnt = 0L;
+                for (Long userId : userIds) {
+                    byte[] redis_key = getUserTokenKey(userId.toString());
+                    connection.del(redis_key);
+                    cnt++;
+                }
+                connection.closePipeline();
+                return cnt;
+            }
+        });
+        return l > 0;
+    }
+
+    /**
+     * 获取用户jwt
+     *
+     * @param userId 用户ID
+     * @return jwt信息
+     */
+    @Override
+    public JwtStore getToken(String userId) {
+        JwtStore tokenStore = null;
+        //从redis中取出
+        final byte[] redis_key = getUserTokenKey(userId);
+        tokenStore = (JwtStore) redisForToken.execute(new RedisCallback<JwtStore>() {
+            @Override
+            public JwtStore doInRedis(RedisConnection connection) throws DataAccessException {
+                byte[] bytes = connection.get(redis_key);
+                if (bytes == null) {
+                    return null;
+                }
+                return (JwtStore) deserializeValue(bytes);
+            }
+        });
+        return tokenStore;
+    }
+
+    @Override
+    public String getUserIDByToken(String token) {
+        DecodedJWT jwt = decodedJWT(token);
+        Map<String, Claim> claims = jwt.getClaims();
+        return claims.get("id").asInt().toString();
+    }
+}

+ 6 - 0
gateway-service/src/main/java/com/lantone/authorization/AuthorizationManager.java

@@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil;
 import com.lantone.common.constant.AuthConstant;
 import com.lantone.common.domain.UserDto;
 import com.lantone.common.service.RedisService;
+import com.lantone.common.service.SysTokenService;
 import com.lantone.common.util.SysUserUtils;
 import com.lantone.config.IgnoreUrlsConfig;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -35,6 +36,8 @@ import java.util.stream.Collectors;
 @Component
 public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
 
+    @Autowired
+    private SysTokenService sysTokenService;
     @Autowired
     private RedisService redisService;
     @Autowired
@@ -61,6 +64,9 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
         if (StrUtil.isEmpty(token)) {
             return Mono.just(new AuthorizationDecision(false));
         }
+        if(!sysTokenService.verifyToken(token.replaceFirst("Bearer ", ""),1)){
+            return Mono.just(new AuthorizationDecision(false));
+        }
         UserDto userDto = SysUserUtils.getUserByToken(token);
         if (userDto == null || (AuthConstant.SECURITY_CENTER_CLIENT_ID.equals(userDto.getClientId())
                 && !pathMatcher.match(AuthConstant.SECURITY_CENTER_URL_PATTERN, uri.getPath()))) {

+ 4 - 0
gateway-service/src/main/java/com/lantone/config/ResourceServerConfig.java

@@ -5,6 +5,7 @@ import com.lantone.authorization.AuthorizationManager;
 import com.lantone.common.constant.AuthConstant;
 import com.lantone.component.RestAuthenticationEntryPoint;
 import com.lantone.component.RestfulAccessDeniedHandler;
+import com.lantone.filter.GateWayFilter;
 import com.lantone.filter.IgnoreUrlsRemoveJwtFilter;
 import lombok.AllArgsConstructor;
 import org.springframework.context.annotation.Bean;
@@ -33,6 +34,7 @@ public class ResourceServerConfig {
 
     private final AuthorizationManager authorizationManager;
     private final IgnoreUrlsConfig ignoreUrlsConfig;
+    private final GateWayFilter gateWayFilter;
     private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;
     private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;
     private final IgnoreUrlsRemoveJwtFilter ignoreUrlsRemoveJwtFilter;
@@ -45,6 +47,8 @@ public class ResourceServerConfig {
         http.oauth2ResourceServer().authenticationEntryPoint(restAuthenticationEntryPoint);
         //对白名单路径,直接移除JWT请求头
         http.addFilterBefore(ignoreUrlsRemoveJwtFilter, SecurityWebFiltersOrder.AUTHENTICATION);
+        //请求头添加uuid防止直接访问服务
+        http.addFilterBefore(gateWayFilter,SecurityWebFiltersOrder.LAST);
         http.authorizeExchange()
                 .pathMatchers(ArrayUtil.toArray(ignoreUrlsConfig.getUrls(), String.class)).permitAll()//白名单配置
                 .anyExchange().access(authorizationManager)//鉴权管理器配置

+ 33 - 0
gateway-service/src/main/java/com/lantone/filter/GateWayFilter.java

@@ -0,0 +1,33 @@
+package com.lantone.filter;
+
+
+import com.lantone.common.constant.AuthConstant;
+import com.lantone.common.service.RedisService;
+import com.lantone.common.util.UUidUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+/**
+ * @Description:
+ * @Author songxl
+ * @Date 2021/9/26
+ */
+@Component
+public class GateWayFilter implements WebFilter {
+    @Autowired
+    private RedisService redisService;
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
+        //请求头添加uuid
+        String uuid = UUidUtil.getUUid();
+        ServerHttpRequest request = exchange.getRequest().mutate().header(AuthConstant.UUID,uuid).build();
+        exchange = exchange.mutate().request(request).build();
+        redisService.set(AuthConstant.UUID,uuid);
+        return chain.filter(exchange);
+    }
+}

+ 1 - 0
gateway-service/src/main/resources/bootstrap.yml

@@ -80,6 +80,7 @@ secure:
       - "/user-auth/oauth/token"
       - "/user-auth/rsa/publicKey"
       - "/security-center/userManage/login"
+      - "/security-center/userManage/close"
       - "/message-service/pageSample/sample"
       - "/message-service/backSample/sendToDefaultTopic"
       - "/message-service/backSample/sendToTopic"

+ 1 - 0
pom.xml

@@ -28,6 +28,7 @@
         <module>dblayer-mbg</module>
         <module>user-auth</module>
         <module>message-service</module>
+        <module>bus-interceptor</module>
     </modules>
 
     <properties>

+ 5 - 0
security-center/pom.xml

@@ -14,6 +14,11 @@
     <packaging>jar</packaging>
 
     <dependencies>
+        <dependency>
+            <groupId>com.lantone</groupId>
+            <artifactId>bus-interceptor</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
         <dependency>
             <groupId>com.lantone</groupId>
             <artifactId>dblayer-mbg</artifactId>

+ 1 - 0
security-center/src/main/java/com/lantone/security/facade/NoticeManagementFacade.java

@@ -167,6 +167,7 @@ public class NoticeManagementFacade {
             noticeUsers.add(noticeUser);
             SendToTopicDTO sendToTopicDTO = new SendToTopicDTO();
             sendToTopicDTO.setMessage("+1");
+            sendToTopicDTO.setType("count");
             sendToTopicDTO.setTopic(userId+"");
             sendToTopics.add(sendToTopicDTO);
         });

+ 50 - 1
security-center/src/main/java/com/lantone/security/facade/UserManagementFacade.java

@@ -1,8 +1,10 @@
 package com.lantone.security.facade;
 
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.crypto.digest.BCrypt;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.auth0.jwt.interfaces.Claim;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -16,7 +18,9 @@ import com.lantone.common.dto.GetUserDTO;
 import com.lantone.common.dto.GetUserHospitalDeptDTO;
 import com.lantone.common.dto.GetUserHospitalsDTO;
 import com.lantone.common.dto.GetUserPageDTO;
+import com.lantone.common.dto.JwtStore;
 import com.lantone.common.dto.LoginLogDTO;
+import com.lantone.common.dto.SendToTopicDTO;
 import com.lantone.common.dto.SoftwareDTO;
 import com.lantone.common.dto.UserInfoDTO;
 import com.lantone.common.dto.UserRoleDTO;
@@ -25,6 +29,8 @@ import com.lantone.common.enums.DataAuthDataTypeEnum;
 import com.lantone.common.enums.IsDeleteEnum;
 import com.lantone.common.enums.StatusEnum;
 import com.lantone.common.exception.Asserts;
+import com.lantone.common.service.RedisService;
+import com.lantone.common.service.SysTokenService;
 import com.lantone.common.util.DateUtil;
 import com.lantone.common.util.EntityUtil;
 import com.lantone.common.util.HttpUtils;
@@ -110,6 +116,10 @@ public class UserManagementFacade {
     private DictionaryInfoFacade dictionaryInfoFacade;
     @Autowired
     private MessageService messageService;
+    @Autowired
+    private RedisService redisService;
+    @Autowired
+    private SysTokenService sysTokenService;
 
     public CommonResult login(LoginVO loginVO) {
         Map<String, String> params = new HashMap<>();
@@ -119,10 +129,40 @@ public class UserManagementFacade {
         params.put("username", loginVO.getUsername());
         params.put("password", loginVO.getPassword());
         CommonResult restResult = authService.getAccessToken(params);
+        userLoginCheck(restResult);
         sendLogRecordMessage(restResult);
         return restResult;
     }
 
+    private void userLoginCheck(CommonResult restResult) {
+        if (restResult.getData() != null) {
+            Map<String, Object> map = JSONObject.parseObject(JSON.toJSONString(restResult.getData()));
+            if (map.containsKey(AuthConstant.ASSESS_TOKEN) && null != map.get(AuthConstant.ASSESS_TOKEN)) {
+                String userId = sysTokenService.getUserIDByToken(map.get(AuthConstant.ASSESS_TOKEN).toString());
+                JwtStore oldJwt = sysTokenService.getToken(userId);
+                String ip = HttpUtils.getIpAddress();
+                //异地登录
+                if (StringUtil.isNotBlank(ip)
+                        && oldJwt != null
+                        && !ip.equals(oldJwt.getIp())) {
+                    //推送消息
+                    List<SendToTopicDTO> sendToTopics = new ArrayList<>();
+                    SendToTopicDTO sendToTopicDTO = new SendToTopicDTO();
+                    sendToTopicDTO.setMessage("温誓提示,您的账号在其它地方已登录您将被迫下线!");
+                    sendToTopicDTO.setType("login");
+                    sendToTopicDTO.setTopic(userId);
+                    sendToTopics.add(sendToTopicDTO);
+                    messageService.sendToTopic(sendToTopics);
+                }
+                JwtStore jwtStore = new JwtStore();
+                jwtStore.setAccessToken(map.get(AuthConstant.ASSESS_TOKEN).toString());
+                jwtStore.setRefreshToken(map.get(AuthConstant.REFRESH_TOKEN).toString());
+                jwtStore.setIp(HttpUtils.getIpAddress());
+                sysTokenService.createToken(jwtStore);
+            }
+        }
+    }
+
     public UserDto loadUserByUsername(String username) {
         QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
         userQueryWrapper.eq("username", username);
@@ -211,7 +251,7 @@ public class UserManagementFacade {
         } else {
             if (ListUtil.isNotEmpty(users)) {
                 users.stream().forEach(user1 -> {
-                    if(!user.getId().equals(user1.getId())){
+                    if (!user.getId().equals(user1.getId())) {
                         Asserts.fail("该用户已存在,请更换用户名");
                     }
                 });
@@ -823,4 +863,13 @@ public class UserManagementFacade {
         }
     }
 
+    /**
+     *@Description用户登出
+     *
+     * @param userId
+     * @Return com.lantone.common.api.CommonResult<java.lang.Boolean>
+     */
+    public CommonResult<Boolean> close(String userId) {
+        return CommonResult.success(sysTokenService.deleteToken(userId));
+    }
 }

+ 5 - 0
security-center/src/main/java/com/lantone/security/web/UserManagementController.java

@@ -47,6 +47,11 @@ public class UserManagementController {
     public CommonResult login(@Valid @RequestBody LoginVO loginVO) {
         return userManagementFacade.login(loginVO);
     }
+    @ApiOperation(value = "登出")
+    @PostMapping(value = "/close")
+    public CommonResult<Boolean> close(@RequestParam @Valid @NotNull(message = "用户编号为空") String userId) {
+        return userManagementFacade.close(userId);
+    }
 
     @ApiOperation("根据用户名获取通用用户信息")
     @PostMapping(value = "/loadByUsername")

+ 6 - 0
user-auth/src/main/java/com/lantone/UserAuthApplication.java

@@ -1,13 +1,19 @@
 package com.lantone;
 
+import com.lantone.common.service.SysTokenService;
+import com.lantone.common.service.impl.SysTokenServiceImpl;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
 import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.FilterType;
 
 @EnableFeignClients
 @EnableEurekaClient
 @SpringBootApplication
+@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
+        classes = {SysTokenService.class, SysTokenServiceImpl.class})})
 public class UserAuthApplication {
 
     public static void main(String[] args) {

+ 2 - 1
user-auth/src/main/resources/bootstrap.yml

@@ -33,4 +33,5 @@ ribbon:
 
 swagger.title: 认证中心
 swagger.des: 认证中心相关接口文档
-swagger.version: 0.0.1-SNAPSHOT
+swagger.version: 0.0.1-SNAPSHOT
+swagger.show: true