2021与蓝度共同重构项目,服务端
chenjiantian
2022-01-17 a2f23bc97d133bcf7b950cd269eda577be4c0cac
token优化
已添加6个文件
已修改22个文件
1040 ■■■■■ 文件已修改
dao/src/main/java/com/sandu/ximon/dao/bo/MenuNode.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/enums/ResponseStatusEnums.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/execption/ApiError.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/execption/BusinessException.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/execption/EntityExistException.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/log/LogService.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/redis/UserModel.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/redis/online/OnlineUser.java 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/redis/online/OnlineUserService.java 226 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/security/JwtAccessDeniedHandler.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/security/JwtAuthenticationEntryPoint.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/security/LoginUserInfo.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/security/TokenFilter.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/security/config/SecurityConfig.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/security/config/SecurityProperties.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/security/jwt/JwtTokenProvider.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/security/token/JwtTokenProvider.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/security/token/RedisTokenProvider.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/security/token/TokenProvider.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/service/BaseService.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/service/impl/BaseServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/util/RequestHolder.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/util/SpringContextHolder.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sandu-common/src/main/java/com/sandu/common/util/ThrowableUtil.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ximon-admin/src/main/java/com/sandu/ximon/admin/controller/AdminController.java 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ximon-admin/src/main/java/com/sandu/ximon/admin/controller/MenuController.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ximon-admin/src/main/java/com/sandu/ximon/admin/security/SecurityUtils.java 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ximon-admin/src/main/resources/application-test.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
dao/src/main/java/com/sandu/ximon/dao/bo/MenuNode.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.sandu.ximon.dao.bo;
import com.sandu.ximon.dao.domain.Menu;
import lombok.Data;
import java.util.List;
/**
 * èœå•树形节点
 *
 * @author chenjiantian
 * @date 2021/4/27 19:00
 */
@Data
public class MenuNode extends Menu {
    private List<MenuNode> children;
}
sandu-common/src/main/java/com/sandu/common/enums/ResponseStatusEnums.java
@@ -21,6 +21,7 @@
     */
    TOKEN_INVALID(40011, "token无效"),
    TOKEN_EXPIRED(40012, "token过期"),
    TOKEN_NONE(40013, "token为空"),
    SINGLE_LOGIN(40014, "该账号在其他设备登录,请重新登录"),
    FORBIDDEN(40030, "没有相关权限"),
sandu-common/src/main/java/com/sandu/common/execption/ApiError.java
@@ -1,9 +1,4 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.execption;
import com.fasterxml.jackson.annotation.JsonFormat;
sandu-common/src/main/java/com/sandu/common/execption/BusinessException.java
@@ -1,9 +1,4 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.execption;
import com.sandu.common.enums.ResponseStatusEnums;
sandu-common/src/main/java/com/sandu/common/execption/EntityExistException.java
@@ -1,9 +1,4 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.execption;
import org.springframework.util.StringUtils;
sandu-common/src/main/java/com/sandu/common/log/LogService.java
@@ -1,9 +1,4 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.log;
import org.aspectj.lang.ProceedingJoinPoint;
sandu-common/src/main/java/com/sandu/common/redis/UserModel.java
@@ -1,14 +1,21 @@
package com.sandu.common.redis;
import com.sandu.common.security.config.SecurityProperties;
import com.sandu.common.util.SpringContextHolder;
/**
 * @author chenjiantian
 * @date 2021/7/20 12:00
 */
public class UserModel extends BasePrefix{
public class UserModel extends BasePrefix {
    @Override
    public String getModelName() {
        return "user";
        String onlineKey = SpringContextHolder.getBean(SecurityProperties.class).getOnlineKey();
        if (onlineKey == null) {
            throw new IllegalArgumentException("请配置onlineKey");
        }
        return onlineKey;
    }
    public UserModel(int expireSeconds, String prefix) {
@@ -17,5 +24,8 @@
    public static UserModel User = new UserModel(60, "gl");
    public static UserModel USER_TOKEN = new UserModel(60 * 60 * 24 * 7, "token");
    public static UserModel USER_INFO = new UserModel(60 * 60 * 24 * 7, "info");
}
sandu-common/src/main/java/com/sandu/common/redis/online/OnlineUser.java
@@ -1,26 +1,26 @@
package com.sandu.common.redis.online;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
 * @author chenjiantian
 * @date 2021/8/6 16:31
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OnlineUser {
    private Long userId;
    // è¿™é‡ŒæŒ‡æ‰‹æœºå·
    private String userName;
    private String token;
    private String ip;
    private String browser;
    private LocalDateTime loginTime;
}
//package com.sandu.common.redis.online;
//
//import lombok.AllArgsConstructor;
//import lombok.Data;
//import lombok.NoArgsConstructor;
//
//import java.time.LocalDateTime;
//
///**
// * @author chenjiantian
// * @date 2021/8/6 16:31
// */
//@Data
//@AllArgsConstructor
//@NoArgsConstructor
//public class OnlineUser {
//
//    private Long userId;
//    // è¿™é‡ŒæŒ‡æ‰‹æœºå·
//    private String userName;
//    private String token;
//    private String ip;
//    private String browser;
//    private LocalDateTime loginTime;
//
//}
sandu-common/src/main/java/com/sandu/common/redis/online/OnlineUserService.java
@@ -1,115 +1,115 @@
package com.sandu.common.redis.online;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentInfo;
import cn.hutool.http.useragent.UserAgentUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.sandu.common.redis.RedisService;
import com.sandu.common.security.config.SecurityProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
 * @author chenjiantian
 * @date 2021/8/6 16:26
 * åœ¨çº¿ç”¨æˆ·æœåŠ¡ç±»
 * åœ¨ BaseRedisConfig æ³¨å…¥
 */
@Slf4j
public class OnlineUserService {
    private final SecurityProperties properties;
    private final RedisService redisService;
    public OnlineUserService(SecurityProperties properties, RedisService redisService) {
        this.properties = properties;
        this.redisService = redisService;
    }
    /**
     * ä¿å­˜åœ¨çº¿ç”¨æˆ·ä¿¡æ¯
     */
    public void save(UserDetails userDetails, String token, Long userId,HttpServletRequest request) {
        String ip = ServletUtil.getClientIP(request);
        UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
        String browser = Optional.ofNullable(userAgent).map(UserAgent::getBrowser).map(UserAgentInfo::getName).orElse("");
        OnlineUser onlineUser = new OnlineUser(userId,userDetails.getUsername(), token, ip, browser, LocalDateTime.now());
//        redisService.set(properties.getOnlineKey() + token, onlineUser, properties.getTokenValidityInSeconds() / 1000);
        redisService.set(properties.getOnlineKey() + userDetails.getUsername(), onlineUser);
    }
    /**
     * æŸ¥è¯¢å…¨éƒ¨æ•°æ®
     *
     * @param filter
     * @return
     */
    public List<OnlineUser> getAll(String filter) {
        Set<String> keys = redisService.keys(properties.getOnlineKey() + "*");
        List<OnlineUser> onlineUsers = new ArrayList<>();
        for (String key : keys) {
            OnlineUser onlineUser = (OnlineUser) redisService.get(key);
            if (StringUtils.isNotBlank(filter)) {
                if (onlineUser.toString().contains(filter)) {
                    onlineUsers.add(onlineUser);
                }
            } else {
                onlineUsers.add(onlineUser);
            }
        }
        onlineUsers.sort((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime()));
        return onlineUsers;
    }
    private boolean kickOut(String token) throws Exception {
        token = properties.getOnlineKey() + token;
        return redisService.del(token);
    }
    public OnlineUser getOne(String key) {
        return (OnlineUser) redisService.get(key);
    }
    /**
     * æ£€æµ‹ç”¨æˆ·æ˜¯å¦åœ¨ä¹‹å‰å·²ç»ç™»å½•,已经登录踢下线
     *
     * @param userName ç”¨æˆ·å
     */
    public void checkLoginOnUser(String userName, String ignoreToken) {
//        List<OnlineUser> onlineUsers = getAll(userName);
//        if (onlineUsers == null || onlineUsers.isEmpty()) {
//package com.sandu.common.redis.online;
//
//import cn.hutool.extra.servlet.ServletUtil;
//import cn.hutool.http.useragent.UserAgent;
//import cn.hutool.http.useragent.UserAgentInfo;
//import cn.hutool.http.useragent.UserAgentUtil;
//import com.baomidou.mybatisplus.core.toolkit.StringUtils;
//import com.sandu.common.redis.RedisService;
//import com.sandu.common.security.config.SecurityProperties;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.security.core.userdetails.UserDetails;
//
//import javax.servlet.http.HttpServletRequest;
//import java.time.LocalDateTime;
//import java.util.ArrayList;
//import java.util.List;
//import java.util.Optional;
//import java.util.Set;
//
///**
// * @author chenjiantian
// * @date 2021/8/6 16:26
// * åœ¨çº¿ç”¨æˆ·æœåŠ¡ç±»
// * åœ¨ BaseRedisConfig æ³¨å…¥
// */
//@Slf4j
//public class OnlineUserService {
//
//    private final SecurityProperties properties;
//    private final RedisService redisService;
//
//    public OnlineUserService(SecurityProperties properties, RedisService redisService) {
//        this.properties = properties;
//        this.redisService = redisService;
//    }
//
//    /**
//     * ä¿å­˜åœ¨çº¿ç”¨æˆ·ä¿¡æ¯
//     */
//    public void save(UserDetails userDetails, String token, Long userId,HttpServletRequest request) {
//        String ip = ServletUtil.getClientIP(request);
//        UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
//        String browser = Optional.ofNullable(userAgent).map(UserAgent::getBrowser).map(UserAgentInfo::getName).orElse("");
//        OnlineUser onlineUser = new OnlineUser(userId,userDetails.getUsername(), token, ip, browser, LocalDateTime.now());
////        redisService.set(properties.getOnlineKey() + token, onlineUser, properties.getTokenValidityInSeconds() / 1000);
//        redisService.set(properties.getOnlineKey() + userDetails.getUsername(), onlineUser);
//    }
//
//    /**
//     * æŸ¥è¯¢å…¨éƒ¨æ•°æ®
//     *
//     * @param filter
//     * @return
//     */
//    public List<OnlineUser> getAll(String filter) {
//        Set<String> keys = redisService.keys(properties.getOnlineKey() + "*");
//
//        List<OnlineUser> onlineUsers = new ArrayList<>();
//        for (String key : keys) {
//            OnlineUser onlineUser = (OnlineUser) redisService.get(key);
//            if (StringUtils.isNotBlank(filter)) {
//                if (onlineUser.toString().contains(filter)) {
//                    onlineUsers.add(onlineUser);
//                }
//            } else {
//                onlineUsers.add(onlineUser);
//            }
//        }
//        onlineUsers.sort((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime()));
//        return onlineUsers;
//    }
//
//    private boolean kickOut(String token) throws Exception {
//        token = properties.getOnlineKey() + token;
//        return redisService.del(token);
//    }
//
//    public OnlineUser getOne(String key) {
//        return (OnlineUser) redisService.get(key);
//    }
//
//    /**
//     * æ£€æµ‹ç”¨æˆ·æ˜¯å¦åœ¨ä¹‹å‰å·²ç»ç™»å½•,已经登录踢下线
//     *
//     * @param userName ç”¨æˆ·å
//     */
//    public void checkLoginOnUser(String userName, String ignoreToken) {
////        List<OnlineUser> onlineUsers = getAll(userName);
////        if (onlineUsers == null || onlineUsers.isEmpty()) {
////            return;
////        }
//        OnlineUser onlineUser = getOne(properties.getOnlineKey() + userName);
//        if(onlineUser == null){
//            return;
//        }
        OnlineUser onlineUser = getOne(properties.getOnlineKey() + userName);
        if(onlineUser == null){
            return;
        }
//        for (OnlineUser onlineUser : onlineUsers) {
            if (onlineUser.getUserName().equals(userName)) {
                try {
                    String token = onlineUser.getToken();
                    if (StringUtils.isNotBlank(ignoreToken) && !ignoreToken.equals(token)) {
                        this.kickOut(token);
                    } else if (StringUtils.isBlank(ignoreToken)) {
                        this.kickOut(token);
                    }
                } catch (Exception e) {
                    log.error("checkUser is error", e);
                }
            }
//        }
    }
    public void updateOne(OnlineUser onlineUser) {
        redisService.set(properties.getOnlineKey() + onlineUser.getUserName(), onlineUser);
    }
}
////        for (OnlineUser onlineUser : onlineUsers) {
//            if (onlineUser.getUserName().equals(userName)) {
//                try {
//                    String token = onlineUser.getToken();
//                    if (StringUtils.isNotBlank(ignoreToken) && !ignoreToken.equals(token)) {
//                        this.kickOut(token);
//                    } else if (StringUtils.isBlank(ignoreToken)) {
//                        this.kickOut(token);
//                    }
//                } catch (Exception e) {
//                    log.error("checkUser is error", e);
//                }
//            }
////        }
//    }
//
//    public void updateOne(OnlineUser onlineUser) {
//        redisService.set(properties.getOnlineKey() + onlineUser.getUserName(), onlineUser);
//    }
//}
sandu-common/src/main/java/com/sandu/common/security/JwtAccessDeniedHandler.java
@@ -1,9 +1,4 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.security;
import org.springframework.security.access.AccessDeniedException;
sandu-common/src/main/java/com/sandu/common/security/JwtAuthenticationEntryPoint.java
@@ -1,20 +1,17 @@
package com.sandu.common.security;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.sandu.common.enums.ResponseStatusEnums;
import com.sandu.common.redis.online.OnlineUser;
import com.sandu.common.redis.online.OnlineUserService;
import com.sandu.common.security.config.SecurityProperties;
import com.sandu.common.security.jwt.JwtTokenProvider;
import com.sandu.common.security.token.TokenProvider;
import com.sandu.common.util.ResponseUtil;
import com.sandu.common.util.SpringContextHolder;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -25,70 +22,91 @@
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Autowired
    private JwtTokenProvider tokenProvider;
    @Resource
    private TokenProvider tokenProvider;
    @Resource
    private SecurityProperties properties;
    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException authException) throws IOException {
        String token = tokenProvider.getToken(request);
        SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
        String token = request.getHeader(properties.getHeader());
        if (!StringUtils.hasText(token)) {
            writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_NONE.getCode(), ResponseStatusEnums.TOKEN_NONE.getMessage()));
            return;
        }
        LoginUserInfo loginUserInfo = tokenProvider.validateToken(token);
        if (loginUserInfo == null) {
            writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage()));
            return;
        }
        Throwable exception = tokenProvider.getException(token);
        // jwt过期 æ›¿æ¢æ–°çš„jwt
        if (exception instanceof ExpiredJwtException) {
            String usernameForce = tokenProvider.getSubjectForce(token);
            if (properties.isCacheOnline()) {
                OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class);
                OnlineUser onlineUser = onlineUserService.getOne(properties.getOnlineKey() + usernameForce);
                if (onlineUser == null || !token.equals(onlineUser.getToken())) {
                    // redis上没有 æˆ–者不是当前用户的token è¿”回失效
                    writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage()));
                } else {
                    // è¿”回过期,携带新的token
                    String refreshToken = tokenProvider.refreshToken(token);
                    onlineUser.setToken(refreshToken);
                    onlineUserService.updateOne(onlineUser);
                    writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_EXPIRED.getCode(), ResponseStatusEnums.TOKEN_EXPIRED.getMessage(), properties.getTokenStartWith() + refreshToken));
                }
            } else {
                // è¿”回过期,携带新的token
                String refreshToken = properties.getTokenStartWith() + tokenProvider.refreshToken(token);
                writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_EXPIRED.getCode(), ResponseStatusEnums.TOKEN_EXPIRED.getMessage(), null));
            }
            return;
        if (!StrUtil.equals(loginUserInfo.getToken(), token)) {
            writeResponse(response, ResponseUtil.error(ResponseStatusEnums.SINGLE_LOGIN.getCode(), ResponseStatusEnums.SINGLE_LOGIN.getMessage()));
        }
        // å…¶ä»–jwt解析异常
        if (exception != null) {
            writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage()));
        } else {
            //jwt解析正常 çœ‹çœ‹æ˜¯ä¸æ˜¯redis的问题
            if (properties.isCacheOnline()) {
                String usernameForce = tokenProvider.getSubjectForce(token);
                OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class);
                OnlineUser onlineUser = onlineUserService.getOne(properties.getOnlineKey() + usernameForce);
                if (onlineUser != null && !token.equals(onlineUser.getToken())) {
                    // å’ŒæœåŠ¡å™¨ä¿å­˜çš„token不一样,说明在其他地方登录
                    writeResponse(response, ResponseUtil.error(ResponseStatusEnums.SINGLE_LOGIN.getCode(), ResponseStatusEnums.SINGLE_LOGIN.getMessage()));
                } else {
                    // æœåŠ¡å™¨æ²¡æœ‰åœ¨çº¿ç”¨æˆ·token或者token不一致 éƒ½è¿”回token失效
                    writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage()));
                }
            } else {
                writeResponse(response, ResponseUtil.error(ResponseStatusEnums.FAIL.getCode(), "认证出错"));
            }
        }
        return;
//        String token = tokenProvider.getToken(request);
//        SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
//
//        if (!StringUtils.hasText(token)) {
//            writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage()));
//            return;
//        }
//
//
//        Throwable exception = tokenProvider.getException(token);
//
//        // jwt过期 æ›¿æ¢æ–°çš„jwt
//        if (exception instanceof ExpiredJwtException) {
//            String usernameForce = tokenProvider.getSubjectForce(token);
//            if (properties.isCacheOnline()) {
//                OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class);
//                OnlineUser onlineUser = onlineUserService.getOne(properties.getOnlineKey() + usernameForce);
//                if (onlineUser == null || !token.equals(onlineUser.getToken())) {
//                    // redis上没有 æˆ–者不是当前用户的token è¿”回失效
//                    writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage()));
//                } else {
//                    // è¿”回过期,携带新的token
//                    String refreshToken = tokenProvider.refreshToken(token);
//                    onlineUser.setToken(refreshToken);
//                    onlineUserService.updateOne(onlineUser);
//                    writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_EXPIRED.getCode(), ResponseStatusEnums.TOKEN_EXPIRED.getMessage(), properties.getTokenStartWith() + refreshToken));
//                }
//            } else {
//                // è¿”回过期,携带新的token
//                String refreshToken = properties.getTokenStartWith() + tokenProvider.refreshToken(token);
//                writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_EXPIRED.getCode(), ResponseStatusEnums.TOKEN_EXPIRED.getMessage(), null));
//            }
//            return;
//        }
//
//        // å…¶ä»–jwt解析异常
//        if (exception != null) {
//            writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage()));
//        } else {
//            //jwt解析正常 çœ‹çœ‹æ˜¯ä¸æ˜¯redis的问题
//            if (properties.isCacheOnline()) {
//                String usernameForce = tokenProvider.getSubjectForce(token);
//                OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class);
//                OnlineUser onlineUser = onlineUserService.getOne(properties.getOnlineKey() + usernameForce);
//                if (onlineUser != null && !token.equals(onlineUser.getToken())) {
//                    // å’ŒæœåŠ¡å™¨ä¿å­˜çš„token不一样,说明在其他地方登录
//                    writeResponse(response, ResponseUtil.error(ResponseStatusEnums.SINGLE_LOGIN.getCode(), ResponseStatusEnums.SINGLE_LOGIN.getMessage()));
//                } else {
//                    // æœåŠ¡å™¨æ²¡æœ‰åœ¨çº¿ç”¨æˆ·token或者token不一致 éƒ½è¿”回token失效
//                    writeResponse(response, ResponseUtil.error(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage()));
//                }
//            } else {
//                writeResponse(response, ResponseUtil.error(ResponseStatusEnums.FAIL.getCode(), "认证出错"));
//            }
//        }
//        return;
    }
sandu-common/src/main/java/com/sandu/common/security/LoginUserInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
package com.sandu.common.security;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sandu.common.enums.AdminStatusStatus;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
/**
 * @author chenjiantian
 * @date 2022/1/8 16:22
 * ç™»å½•用户的信息
 */
@Data
public class LoginUserInfo implements UserDetails {
    private Long userId;
    private String account;
    private String name;
    private String token;
    private String role;
    /**
     * ç”¨æˆ·æƒé™ ç”¨,分隔
     */
    private String permission;
    /**
     * 0 "禁用",  1 "正常" ,2:token è¿‡æœŸ 3:未找到用户信息 4:在其它地方登录 5:IP限制 6:token è§£æžå¤±è´¥
     */
    private Integer status;
    private Long createTimestamp;
    private String ip;
    // ç®¡ç†å‘˜ç±»åž‹ï¼Œç®¡ç†å‘˜ 0官方管理员,1客户管理员 ã€‚查看AdministratorEnums
    private Integer administratorType;
    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if (StrUtil.isBlank(permission)) {
            return null;
        }
        return Arrays.stream(permission.split(","))
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
    }
    @JsonIgnore
    @Override
    public String getPassword() {
        return null;
    }
    @JsonIgnore
    @Override
    public String getUsername() {
        return account;
    }
    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return AdminStatusStatus.NORMAL.getCode().equals(status);
    }
}
sandu-common/src/main/java/com/sandu/common/security/TokenFilter.java
@@ -1,22 +1,19 @@
package com.sandu.common.security;
import com.sandu.common.redis.online.OnlineUser;
import com.sandu.common.redis.online.OnlineUserService;
import cn.hutool.core.util.StrUtil;
import com.sandu.common.security.config.SecurityProperties;
import com.sandu.common.security.jwt.JwtTokenProvider;
import com.sandu.common.util.SpringContextHolder;
import com.sandu.common.security.token.TokenProvider;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
@@ -26,40 +23,32 @@
 * @author chenjiantian
 */
@Slf4j
public class TokenFilter extends GenericFilterBean {
@AllArgsConstructor
public class TokenFilter extends OncePerRequestFilter {
    private final JwtTokenProvider tokenProvider;
    public TokenFilter(JwtTokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }
    private final TokenProvider tokenProvider;
    private final SecurityProperties properties;
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String token = tokenProvider.getToken(httpServletRequest);
        SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader(properties.getHeader());
        if (token != null && token.startsWith(properties.getTokenStartWith())) {
            token = token.substring(properties.getTokenStartWith().length());
        }
        if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
            Authentication authentication = tokenProvider.getAuthentication(token);
            // æ˜¯å¦ä½¿ç”¨redis缓存用户功能
            if (!properties.isCacheOnline()) {
        LoginUserInfo loginUserInfo = tokenProvider.validateToken(token);
        if (loginUserInfo != null) {
            if (StrUtil.equals(loginUserInfo.getToken(), token)) {
                Authentication authentication = new UsernamePasswordAuthenticationToken(
                        loginUserInfo, null,
                        loginUserInfo.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authentication);
            } else {
                OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class);
                OnlineUser onlineUser = onlineUserService.getOne(properties.getOnlineKey() + ((UserDetails) authentication.getPrincipal()).getUsername());
                if (onlineUser != null) {
                    // åŒ¹é…ä¸Šå½“前用户的token才合法,防止jwt不受控制
                    if (token.equals(onlineUser.getToken())) {
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                }
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);
        filterChain.doFilter(request, response);
    }
}
sandu-common/src/main/java/com/sandu/common/security/config/SecurityConfig.java
@@ -1,16 +1,11 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.security.config;
import com.sandu.common.security.JwtAccessDeniedHandler;
import com.sandu.common.security.JwtAuthenticationEntryPoint;
import com.sandu.common.security.TokenFilter;
import com.sandu.common.security.annotation.AnonymousAccess;
import com.sandu.common.security.jwt.JwtTokenProvider;
import com.sandu.common.security.token.TokenProvider;
import lombok.AllArgsConstructor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -41,25 +36,15 @@
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final JwtTokenProvider tokenProvider;
    private final TokenProvider tokenProvider;
    private final CorsFilter corsFilter;
    private final SecurityProperties properties;
    private final JwtAuthenticationEntryPoint authenticationErrorHandler;
    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
    private final ApplicationContext applicationContext;
    public SecurityConfig(JwtTokenProvider tokenProvider, CorsFilter corsFilter,
                          JwtAuthenticationEntryPoint authenticationErrorHandler,
                          JwtAccessDeniedHandler jwtAccessDeniedHandler,
                          ApplicationContext applicationContext) {
        this.tokenProvider = tokenProvider;
        this.corsFilter = corsFilter;
        this.authenticationErrorHandler = authenticationErrorHandler;
        this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
        this.applicationContext = applicationContext;
    }
    @Bean
    GrantedAuthorityDefaults grantedAuthorityDefaults() {
@@ -143,7 +128,6 @@
    @Bean
    public TokenFilter jwtAuthenticationTokenFilter() {
        TokenFilter customFilter = new TokenFilter(tokenProvider);
        return customFilter;
        return new TokenFilter(tokenProvider, properties);
    }
}
sandu-common/src/main/java/com/sandu/common/security/config/SecurityProperties.java
@@ -1,5 +1,6 @@
package com.sandu.common.security.config;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -36,7 +37,7 @@
    private Long tokenValidityInSeconds;
    /**
     * åœ¨çº¿ç”¨æˆ· key,根据 key æŸ¥è¯¢ redis ä¸­åœ¨çº¿ç”¨æˆ·çš„æ•°æ®
     * åœ¨çº¿ç”¨æˆ· model name
     */
    private String onlineKey;
@@ -46,6 +47,9 @@
    private boolean cacheOnline = false;
    public String getTokenStartWith() {
        if(StrUtil.isBlank(tokenStartWith)){
            return "";
        }
        return tokenStartWith + " ";
    }
}
sandu-common/src/main/java/com/sandu/common/security/jwt/JwtTokenProvider.java
@@ -25,7 +25,6 @@
 * @date 2021/4/23 16:43
 */
@Slf4j
@Component
public class JwtTokenProvider implements InitializingBean {
    private static final String AUTHORITIES_KEY = "auth";
sandu-common/src/main/java/com/sandu/common/security/token/JwtTokenProvider.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package com.sandu.common.security.token;
import com.sandu.common.enums.AdminStatusStatus;
import com.sandu.common.security.LoginUserInfo;
import com.sandu.common.security.config.SecurityProperties;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.DecodingException;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
import java.util.stream.Collectors;
/**
 * @author chenjiantian
 * @date 2022/1/8 18:12
 */
@Component
@Slf4j
@ConditionalOnProperty(prefix = "sandu.jwt", name = "cache-online", havingValue = "false")
public class JwtTokenProvider implements TokenProvider, InitializingBean {
    private static final String AUTHORITIES_KEY = "auth";
    private static final String CREDENTIALS_KEY = "cred";
    private static final String ADMINISTRATOR_KEY = "admin";
    private final SecurityProperties properties;
    private Key key;
    public JwtTokenProvider(SecurityProperties properties) {
        this.properties = properties;
    }
    @Override
    public void afterPropertiesSet() {
        byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret());
        this.key = Keys.hmacShaKeyFor(keyBytes);
    }
    @Override
    public String createToken(LoginUserInfo loginUserInfo) {
        if (loginUserInfo.getUserId() == null) {
            throw new IllegalArgumentException("用户id不能为空");
        }
        String authorities = loginUserInfo.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.joining(","));
        long now = (new Date()).getTime();
        Date expiration = new Date(now + properties.getTokenValidityInSeconds());
        return Jwts.builder()
                .setSubject(loginUserInfo.getUserId().toString())
                .claim(AUTHORITIES_KEY, authorities)
                .claim(CREDENTIALS_KEY, loginUserInfo.getAccount())
                .claim(ADMINISTRATOR_KEY, loginUserInfo.getAdministratorType())
                .setExpiration(expiration)
                .signWith(key, SignatureAlgorithm.HS512)
                .compact();
    }
    @Override
    public LoginUserInfo validateToken(String token) {
        try {
            Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
            LoginUserInfo loginUserInfo = new LoginUserInfo();
            loginUserInfo.setUserId(Long.valueOf(claims.getSubject()));
            loginUserInfo.setAccount(String.valueOf(claims.get(CREDENTIALS_KEY)));
            loginUserInfo.setPermission(claims.get(AUTHORITIES_KEY).toString());
            loginUserInfo.setAdministratorType(Integer.parseInt(claims.get(ADMINISTRATOR_KEY).toString()));
            loginUserInfo.setToken(token);
            loginUserInfo.setStatus(AdminStatusStatus.NORMAL.getCode());
            return loginUserInfo;
        } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
            log.info("Invalid JWT signature.");
//            e.printStackTrace();
        } catch (ExpiredJwtException e) {
            log.info("Expired JWT token.");
//            e.printStackTrace();
        } catch (UnsupportedJwtException | DecodingException e) {
            log.info("Unsupported JWT token.");
//            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            log.info("JWT token compact of handler are invalid.");
//            e.printStackTrace();
        }
        return null;
    }
}
sandu-common/src/main/java/com/sandu/common/security/token/RedisTokenProvider.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package com.sandu.common.security.token;
import cn.hutool.core.util.IdUtil;
import com.sandu.common.redis.RedisService;
import com.sandu.common.redis.UserModel;
import com.sandu.common.security.LoginUserInfo;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
/**
 * @author chenjiantian
 * @date 2022/1/8 16:39
 */
@AllArgsConstructor
@Component
@ConditionalOnProperty(prefix = "sandu.jwt", name = "cache-online", havingValue = "true")
public class RedisTokenProvider implements TokenProvider {
    private final RedisService redisService;
    @Override
    public String createToken(LoginUserInfo loginUserInfo) {
        if (loginUserInfo.getUserId() == null) {
            throw new IllegalArgumentException("用户id不能为空");
        }
        String token = IdUtil.simpleUUID();
        loginUserInfo.setToken(token);
        loginUserInfo.setCreateTimestamp(System.currentTimeMillis());
        redisService.set(UserModel.USER_TOKEN.key(token), loginUserInfo.getUserId(), UserModel.USER_TOKEN.expireSeconds());
        redisService.set(UserModel.USER_INFO.key(loginUserInfo.getUserId().toString()), loginUserInfo);
        return token;
    }
    @Override
    public LoginUserInfo validateToken(String token) {
        if (token == null) {
            return null;
        }
        Long userId = (Long) redisService.get(UserModel.USER_TOKEN.key(token));
        if (userId == null) {
            return null;
        }
        LoginUserInfo userInfo = (LoginUserInfo) redisService.get(UserModel.USER_INFO.key(userId.toString()));
        if (userInfo != null) {
            if (redisService.getExpire(token) < UserModel.USER_TOKEN.expireSeconds()) {
                redisService.expire(token, UserModel.USER_TOKEN.expireSeconds());
            }
        }
        return userInfo;
    }
}
sandu-common/src/main/java/com/sandu/common/security/token/TokenProvider.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.sandu.common.security.token;
import com.sandu.common.security.LoginUserInfo;
/**
 * @author chenjiantian
 * @date 2022/1/8 16:10
 */
public interface TokenProvider {
    /**
     * ç”Ÿæˆtoken
     * @param loginUserInfo ç”¨æˆ·ä¿¡æ¯
     * @return token
     */
    String createToken(LoginUserInfo loginUserInfo);
    /**
     * éªŒè¯token æœ‰æ²¡æœ‰ä¿å­˜ç”¨æˆ·ä¿¡æ¯
     * @param token token
     * @return null表示token æ²¡æœ‰å…³è”用户信息
     */
    LoginUserInfo validateToken(String token);
}
sandu-common/src/main/java/com/sandu/common/service/BaseService.java
@@ -1,9 +1,4 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.service;
import com.baomidou.mybatisplus.extension.service.IService;
sandu-common/src/main/java/com/sandu/common/service/impl/BaseServiceImpl.java
@@ -1,9 +1,4 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.service.impl;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
sandu-common/src/main/java/com/sandu/common/util/RequestHolder.java
@@ -1,9 +1,4 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.util;
import org.springframework.web.context.request.RequestContextHolder;
sandu-common/src/main/java/com/sandu/common/util/SpringContextHolder.java
@@ -1,9 +1,4 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.util;
import lombok.extern.slf4j.Slf4j;
sandu-common/src/main/java/com/sandu/common/util/ThrowableUtil.java
@@ -1,9 +1,4 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.common.util;
import java.io.PrintWriter;
ximon-admin/src/main/java/com/sandu/ximon/admin/controller/AdminController.java
@@ -10,9 +10,11 @@
import com.sandu.common.enums.ResponseStatusEnums;
import com.sandu.common.execption.BusinessException;
import com.sandu.common.object.BaseConditionVO;
import com.sandu.common.security.LoginUserInfo;
import com.sandu.common.security.annotation.AnonymousAccess;
import com.sandu.common.security.config.SecurityProperties;
import com.sandu.common.security.jwt.JwtTokenProvider;
import com.sandu.common.security.token.TokenProvider;
import com.sandu.common.util.IpUtil;
import com.sandu.common.util.ResponseUtil;
import com.sandu.ximon.admin.dto.AdminJwtUser;
import com.sandu.ximon.admin.param.AdminLoginParam;
@@ -26,9 +28,6 @@
import com.sandu.ximon.dao.enums.AdministratorEnums;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
@@ -56,7 +55,7 @@
    private final PasswordEncoder passwordEncoder;
    private final AdminService adminService;
    private final RoleService roleService;
    private final JwtTokenProvider tokenProvider;
    private final TokenProvider tokenProvider;
    @AnonymousAccess
@@ -70,16 +69,22 @@
            return ResponseUtil.error(ResponseStatusEnums.BAD_CREDENTIALS.getCode(), ResponseStatusEnums.BAD_CREDENTIALS.getMessage());
        }
        UserDetails userJwtUser = new AdminJwtUser(admin, roleService.mapToGrantedAuthorities(admin.getId()));
        if(!userJwtUser.isEnabled()){
        if (!userJwtUser.isEnabled()) {
            return ResponseUtil.error(ResponseStatusEnums.DISABLE_ACCOUNT.getCode(), ResponseStatusEnums.DISABLE_ACCOUNT.getMessage());
        }
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userJwtUser, AdministratorEnums.ADMIN.getCode(), userJwtUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        LoginUserInfo loginUserInfo = new LoginUserInfo();
        loginUserInfo.setUserId(admin.getId());
        loginUserInfo.setName(admin.getNickName());
        loginUserInfo.setAccount(admin.getUsername());
        loginUserInfo.setStatus(admin.getStatus());
        loginUserInfo.setIp(IpUtil.getRealIp());
        loginUserInfo.setAdministratorType(AdministratorEnums.ADMIN.getCode());
        loginUserInfo.setPermission("tenant");
        // ç”Ÿæˆä»¤ç‰Œ
        String token = tokenProvider.createToken(authentication);
        final AdminJwtUser jwtUser = (AdminJwtUser) authentication.getPrincipal();
        String token = tokenProvider.createToken(loginUserInfo);
        // æ›´æ–°æœ€åŽç™»å½•æ—¶é—´
        Admin update = new Admin();
@@ -90,25 +95,20 @@
        // è¿”回 token ä¸Ž ç”¨æˆ·ä¿¡æ¯
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUser);
            put("user", admin);
        }};
//        if (singleLogin) {
//            //踢掉之前已经登录的token
//            onlineUserService.checkLoginOnUser(authUser.getUsername(), token);
//        }
        return ResponseUtil.success(authInfo);
    }
    @GetMapping(value = "/info")
    public ResponseVO<Object> getUserInfo() {
        Integer administratorIdentity = SecurityUtils.getAdministratorIdentity();
        Long userId = SecurityUtils.getUserId();
        Admin admin = adminService.getById(userId);
        List<Role> roles = roleService.listByAdminId(admin.getId());
        List<Menu> menus = menuService.listByRoleIds(roles.stream().map(Role::getId).collect(Collectors.toList()));
        MapBuilder<Object, Object> builder = MapUtil.builder().put("info", admin);
        if(CollectionUtil.isNotEmpty(menus)){
        List<Menu> collect = menus.stream().filter(menu -> menu.getType() == 0).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(menus)) {
            List<Menu> collect = menus.stream().filter(menu -> menu.getType() == 0).collect(Collectors.toList());
            builder.put("menus", collect);
        }
        return ResponseUtil.success(builder.build());
ximon-admin/src/main/java/com/sandu/ximon/admin/controller/MenuController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package com.sandu.ximon.admin.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.sandu.common.domain.ResponseVO;
import com.sandu.common.util.ResponseUtil;
import com.sandu.ximon.admin.service.MenuService;
import com.sandu.ximon.dao.bo.MenuNode;
import com.sandu.ximon.dao.domain.Menu;
import lombok.AllArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * @author chenjiantian
 * @date 2021/4/27 18:47
 */
@AllArgsConstructor
@RestController
@RequestMapping("/admin/menu")
public class MenuController {
    private final MenuService menuService;
//    @GetMapping("treeMenuList")
//    @PreAuthorize("@el.check('menu:list','role:list')")
//    public ResponseVO<Object> treeMenuList() {
//        List<Menu> list = menuService.list(Wrappers.lambdaQuery(Menu.class).eq(Menu::getType, 0));
//        List<MenuNode> menuTrees = menuService.buildTree(list);
//        return ResponseUtil.success(menuTrees);
//    }
//
//    @GetMapping("treeList")
//    public ResponseVO<Object> treeList() {
//        List<Menu> list = menuService.list(Wrappers.lambdaQuery(Menu.class));
//        List<MenuNode> menuTrees = menuService.buildTree(list);
//        return ResponseUtil.success(menuTrees);
//    }
//
//    @PutMapping
//    @PreAuthorize("@el.check('menu:edit')")
//    public ResponseVO<Object> update(@RequestBody Menu resources) {
//
//        boolean b = menuService.updateMenu(resources);
//        return ResponseUtil.success("");
//    }
//    @PostMapping
//    public ResponseVO<Object> addMenu(@RequestBody Menu resources) {
//
//        boolean b = menuService.updateMenu(resources);
//        return ResponseUtil.success("");
//    }
}
ximon-admin/src/main/java/com/sandu/ximon/admin/security/SecurityUtils.java
@@ -1,27 +1,11 @@
/**
 * Copyright (C) 2018-2020
 * All rights reserved, Designed By www.yixiang.co
 * æ³¨æ„ï¼š
 * æœ¬è½¯ä»¶ä¸ºwww.yixiang.co开发研制
 */
package com.sandu.ximon.admin.security;
import cn.hutool.json.JSONObject;
import com.sandu.common.enums.ResponseStatusEnums;
import com.sandu.common.execption.BusinessException;
import com.sandu.common.redis.online.OnlineUser;
import com.sandu.common.redis.online.OnlineUserService;
import com.sandu.common.security.config.SecurityProperties;
import com.sandu.common.security.jwt.JwtTokenProvider;
import com.sandu.common.util.RequestHolder;
import com.sandu.common.util.SpringContextHolder;
import com.sandu.common.security.LoginUserInfo;
import com.sandu.ximon.dao.enums.AdministratorEnums;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import javax.servlet.http.HttpServletRequest;
/**
 * èŽ·å–å½“å‰ç™»å½•çš„ç”¨æˆ·
@@ -34,22 +18,18 @@
    /**
     * èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·ä¿¡æ¯
     */
    private static UserDetails getUserDetails() {
    private static LoginUserInfo getUserDetails() {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            throw new BusinessException(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage());
        }
        Integer administratorIdentity = getAdministratorIdentity();
        if (AdministratorEnums.ADMIN.getCode().equals(administratorIdentity)) {
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            UserDetailsService userDetailsService = SpringContextHolder.getBean("adminDetailsService");
            return userDetailsService.loadUserByUsername(userDetails.getUsername());
        } else if (AdministratorEnums.CUSTOMER.getCode().equals(administratorIdentity)) {
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            UserDetailsService userDetailsService = SpringContextHolder.getBean("customerDetailsService");
            return userDetailsService.loadUserByUsername(userDetails.getUsername());
        if (authentication.getPrincipal() instanceof LoginUserInfo) {
            LoginUserInfo loginUserInfo = (LoginUserInfo) authentication.getPrincipal();
            if (!loginUserInfo.isEnabled()) {
                throw new BusinessException(ResponseStatusEnums.DISABLE_ACCOUNT.getCode(), ResponseStatusEnums.DISABLE_ACCOUNT.getMessage());
            }
            return loginUserInfo;
        }
        return null;
    }
@@ -59,12 +39,11 @@
     * @return å½“前登录用户登录名
     */
    public static String getUsername() {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            throw new BusinessException(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage());
        LoginUserInfo loginUserInfo = getUserDetails();
        if (loginUserInfo != null) {
            return loginUserInfo.getUsername();
        }
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        return userDetails.getUsername();
        return null;
    }
    /**
@@ -73,11 +52,11 @@
     * @return
     */
    public static Integer getAdministratorIdentity() {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
        LoginUserInfo loginUserInfo = getUserDetails();
        if (loginUserInfo == null) {
            throw new BusinessException(ResponseStatusEnums.TOKEN_INVALID.getCode(), ResponseStatusEnums.TOKEN_INVALID.getMessage());
        }
        return (Integer) authentication.getCredentials();
        return loginUserInfo.getAdministratorType();
    }
    /**
@@ -86,39 +65,25 @@
     * @return å½“前登录账号的id
     */
    public static Long getUserId() {
        SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
        if (!properties.isCacheOnline()) {
            UserDetails obj = getUserDetails();
            if (obj == null) {
                return null;
            }
            if (!obj.isEnabled()) {
                throw new BusinessException(ResponseStatusEnums.DISABLE_ACCOUNT.getCode(), ResponseStatusEnums.DISABLE_ACCOUNT.getMessage());
            }
            JSONObject json = new JSONObject(obj);
            return json.getJSONObject("user").get("id", Long.class);
        } else {
            HttpServletRequest httpServletRequest = RequestHolder.getHttpServletRequest();
            JwtTokenProvider tokenProvider = SpringContextHolder.getBean(JwtTokenProvider.class);
            OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class);
            String username = tokenProvider.getSubjectForce(tokenProvider.getToken(httpServletRequest));
            OnlineUser one = onlineUserService.getOne(properties.getOnlineKey() + username);
            if (one != null) {
                return one.getUserId();
            } else {
                return null;
            }
        LoginUserInfo loginUserInfo = getUserDetails();
        if (loginUserInfo != null) {
            return loginUserInfo.getUserId();
        }
        return null;
    }
    /**
     * èŽ·å–å®¢æˆ·è´¦å·çš„id å¦‚果当前身份是运营人员,返回null
     *
     * @return
     */
    public static Long getClientId() {
        Integer administratorIdentity = SecurityUtils.getAdministratorIdentity();
        if (AdministratorEnums.CUSTOMER.getCode().equals(administratorIdentity)) {
            return SecurityUtils.getUserId();
        LoginUserInfo loginUserInfo = getUserDetails();
        if (loginUserInfo == null) {
            return null;
        }
        if (AdministratorEnums.CUSTOMER.getCode().equals(loginUserInfo.getAdministratorType())) {
            return loginUserInfo.getUserId();
        }
        return null;
    }
ximon-admin/src/main/resources/application-test.yml
@@ -25,7 +25,7 @@
    # ä»¤ç‰Œè¿‡æœŸæ—¶é—´ æ­¤å¤„单位/毫秒 ï¼Œå¯åœ¨æ­¤ç½‘站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html 1个月
    token-validity-in-seconds: 2629800000
    # åœ¨çº¿ç”¨æˆ·key
    online-key: online-token
    online-key: light-user
    # æ˜¯å¦å¯åЍredis缓存用户信息
    cache-online: false
  upload: