mirror of https://github.com/elunez/eladmin
				
				
				
			refactor: 优化安全配置和代码结构,移除冗余类,SecurityUtils优化获取用户名与用户ID方式
							parent
							
								
									75df46b5dc
								
							
						
					
					
						commit
						51d9f42273
					
				| 
						 | 
				
			
			@ -22,7 +22,7 @@ import lombok.Data;
 | 
			
		|||
 * @date 2018-11-23
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
class ApiError {
 | 
			
		||||
public class ApiError {
 | 
			
		||||
 | 
			
		||||
    private Integer status = 400;
 | 
			
		||||
    private Long timestamp;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,18 +15,24 @@
 | 
			
		|||
 */
 | 
			
		||||
package me.zhengjie.utils;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.jwt.JWT;
 | 
			
		||||
import cn.hutool.jwt.JWTUtil;
 | 
			
		||||
import com.alibaba.fastjson.JSON;
 | 
			
		||||
import com.alibaba.fastjson.JSONArray;
 | 
			
		||||
import com.alibaba.fastjson.JSONObject;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.zhengjie.exception.BadRequestException;
 | 
			
		||||
import me.zhengjie.utils.enums.DataScopeEnum;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
import org.springframework.security.core.context.SecurityContextHolder;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Value;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetails;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetailsService;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.springframework.web.context.request.RequestContextHolder;
 | 
			
		||||
import org.springframework.web.context.request.ServletRequestAttributes;
 | 
			
		||||
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取当前登录的用户
 | 
			
		||||
| 
						 | 
				
			
			@ -34,8 +40,23 @@ import java.util.List;
 | 
			
		|||
 * @date 2019-01-17
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
@Component
 | 
			
		||||
public class SecurityUtils {
 | 
			
		||||
 | 
			
		||||
    public static String header;
 | 
			
		||||
 | 
			
		||||
    public static String tokenStartWith;
 | 
			
		||||
 | 
			
		||||
    @Value("${jwt.header}")
 | 
			
		||||
    public void setHeader(String header) {
 | 
			
		||||
        SecurityUtils.header = header;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Value("${jwt.token-start-with}")
 | 
			
		||||
    public void setTokenStartWith(String tokenStartWith) {
 | 
			
		||||
        SecurityUtils.tokenStartWith = tokenStartWith;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前登录的用户
 | 
			
		||||
     * @return UserDetails
 | 
			
		||||
| 
						 | 
				
			
			@ -45,34 +66,6 @@ public class SecurityUtils {
 | 
			
		|||
        return userDetailsService.loadUserByUsername(getCurrentUsername());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取系统用户名称
 | 
			
		||||
     *
 | 
			
		||||
     * @return 系统用户名称
 | 
			
		||||
     */
 | 
			
		||||
    public static String getCurrentUsername() {
 | 
			
		||||
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
 | 
			
		||||
        if (authentication == null) {
 | 
			
		||||
            throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期");
 | 
			
		||||
        }
 | 
			
		||||
        if (authentication.getPrincipal() instanceof UserDetails) {
 | 
			
		||||
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
 | 
			
		||||
            return userDetails.getUsername();
 | 
			
		||||
        }
 | 
			
		||||
        throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取系统用户ID
 | 
			
		||||
     * @return 系统用户ID
 | 
			
		||||
     */
 | 
			
		||||
    public static Long getCurrentUserId() {
 | 
			
		||||
        UserDetails userDetails = getCurrentUser();
 | 
			
		||||
        // 将 Java 对象转换为 JSONObject 对象
 | 
			
		||||
        JSONObject jsonObject = (JSONObject) JSON.toJSON(userDetails);
 | 
			
		||||
        return jsonObject.getJSONObject("user").getLong("id");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前用户的数据权限
 | 
			
		||||
     * @return /
 | 
			
		||||
| 
						 | 
				
			
			@ -91,9 +84,62 @@ public class SecurityUtils {
 | 
			
		|||
     */
 | 
			
		||||
    public static String getDataScopeType() {
 | 
			
		||||
        List<Long> dataScopes = getCurrentUserDataScope();
 | 
			
		||||
        if(dataScopes.size() != 0){
 | 
			
		||||
        if(CollUtil.isEmpty(dataScopes)){
 | 
			
		||||
            return "";
 | 
			
		||||
        }
 | 
			
		||||
        return DataScopeEnum.ALL.getValue();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取用户ID
 | 
			
		||||
     * @return 系统用户ID
 | 
			
		||||
     */
 | 
			
		||||
    public static Long getCurrentUserId() {
 | 
			
		||||
        return getCurrentUserId(getToken());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取用户ID
 | 
			
		||||
     * @return 系统用户ID
 | 
			
		||||
     */
 | 
			
		||||
    public static Long getCurrentUserId(String token) {
 | 
			
		||||
        JWT jwt = JWTUtil.parseToken(token);
 | 
			
		||||
        return Long.valueOf(jwt.getPayload("userId").toString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取系统用户名称
 | 
			
		||||
     *
 | 
			
		||||
     * @return 系统用户名称
 | 
			
		||||
     */
 | 
			
		||||
    public static String getCurrentUsername() {
 | 
			
		||||
        return getCurrentUsername(getToken());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取系统用户名称
 | 
			
		||||
     *
 | 
			
		||||
     * @return 系统用户名称
 | 
			
		||||
     */
 | 
			
		||||
    public static String getCurrentUsername(String token) {
 | 
			
		||||
        JWT jwt = JWTUtil.parseToken(token);
 | 
			
		||||
        return jwt.getPayload("sub").toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取Token
 | 
			
		||||
     * @return /
 | 
			
		||||
     */
 | 
			
		||||
    public static String getToken() {
 | 
			
		||||
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder
 | 
			
		||||
                .getRequestAttributes())).getRequest();
 | 
			
		||||
        String bearerToken = request.getHeader(header);
 | 
			
		||||
        if (bearerToken != null && bearerToken.startsWith(tokenStartWith)) {
 | 
			
		||||
            // 去掉令牌前缀
 | 
			
		||||
            return bearerToken.replace(tokenStartWith, "");
 | 
			
		||||
        } else {
 | 
			
		||||
            log.debug("非法Token:{}", bearerToken);
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,89 +13,100 @@
 | 
			
		|||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package me.zhengjie.modules.security.config.bean;
 | 
			
		||||
package me.zhengjie.modules.security.config;
 | 
			
		||||
 | 
			
		||||
import com.wf.captcha.*;
 | 
			
		||||
import com.wf.captcha.base.Captcha;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import me.zhengjie.exception.BadConfigurationException;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import me.zhengjie.exception.BadRequestException;
 | 
			
		||||
import me.zhengjie.modules.security.config.enums.LoginCodeEnum;
 | 
			
		||||
import me.zhengjie.utils.StringUtils;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 配置文件读取
 | 
			
		||||
 *
 | 
			
		||||
 * 登录验证码配置信息
 | 
			
		||||
 * @author liaojinlong
 | 
			
		||||
 * @date loginCode.length0loginCode.length0/6/10 17:loginCode.length6
 | 
			
		||||
 * @date 2025-01-13
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
public class LoginProperties {
 | 
			
		||||
@Configuration
 | 
			
		||||
@ConfigurationProperties(prefix = "login.code")
 | 
			
		||||
public class CaptchaConfig {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 账号单用户 登录
 | 
			
		||||
     * 验证码配置
 | 
			
		||||
     */
 | 
			
		||||
    private boolean singleLogin = false;
 | 
			
		||||
 | 
			
		||||
    private LoginCode loginCode;
 | 
			
		||||
 | 
			
		||||
    public static final String cacheKey = "user-login-cache:";
 | 
			
		||||
 | 
			
		||||
    public boolean isSingleLogin() {
 | 
			
		||||
        return singleLogin;
 | 
			
		||||
    }
 | 
			
		||||
    @Getter
 | 
			
		||||
    private LoginCodeEnum codeType;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取验证码生产类
 | 
			
		||||
     *
 | 
			
		||||
     * @return /
 | 
			
		||||
     * 验证码有效期 分钟
 | 
			
		||||
     */
 | 
			
		||||
    public Captcha getCaptcha() {
 | 
			
		||||
        if (Objects.isNull(loginCode)) {
 | 
			
		||||
            loginCode = new LoginCode();
 | 
			
		||||
            if (Objects.isNull(loginCode.getCodeType())) {
 | 
			
		||||
                loginCode.setCodeType(LoginCodeEnum.ARITHMETIC);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return switchCaptcha(loginCode);
 | 
			
		||||
    }
 | 
			
		||||
    private Long expiration = 5L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码内容长度
 | 
			
		||||
     */
 | 
			
		||||
    private int length = 4;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码宽度
 | 
			
		||||
     */
 | 
			
		||||
    private int width = 111;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码高度
 | 
			
		||||
     */
 | 
			
		||||
    private int height = 36;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码字体
 | 
			
		||||
     */
 | 
			
		||||
    private String fontName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 字体大小
 | 
			
		||||
     */
 | 
			
		||||
    private int fontSize = 25;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 依据配置信息生产验证码
 | 
			
		||||
     *
 | 
			
		||||
     * @param loginCode 验证码配置信息
 | 
			
		||||
     * @return /
 | 
			
		||||
     */
 | 
			
		||||
    private Captcha switchCaptcha(LoginCode loginCode) {
 | 
			
		||||
    public Captcha getCaptcha() {
 | 
			
		||||
        Captcha captcha;
 | 
			
		||||
        switch (loginCode.getCodeType()) {
 | 
			
		||||
        switch (codeType) {
 | 
			
		||||
            case ARITHMETIC:
 | 
			
		||||
                // 算术类型 https://gitee.com/whvse/EasyCaptcha
 | 
			
		||||
                captcha = new FixedArithmeticCaptcha(loginCode.getWidth(), loginCode.getHeight());
 | 
			
		||||
                captcha = new FixedArithmeticCaptcha(width, height);
 | 
			
		||||
                // 几位数运算,默认是两位
 | 
			
		||||
                captcha.setLen(loginCode.getLength());
 | 
			
		||||
                captcha.setLen(length);
 | 
			
		||||
                break;
 | 
			
		||||
            case CHINESE:
 | 
			
		||||
                captcha = new ChineseCaptcha(loginCode.getWidth(), loginCode.getHeight());
 | 
			
		||||
                captcha.setLen(loginCode.getLength());
 | 
			
		||||
                captcha = new ChineseCaptcha(width, height);
 | 
			
		||||
                captcha.setLen(length);
 | 
			
		||||
                break;
 | 
			
		||||
            case CHINESE_GIF:
 | 
			
		||||
                captcha = new ChineseGifCaptcha(loginCode.getWidth(), loginCode.getHeight());
 | 
			
		||||
                captcha.setLen(loginCode.getLength());
 | 
			
		||||
                captcha = new ChineseGifCaptcha(width, height);
 | 
			
		||||
                captcha.setLen(length);
 | 
			
		||||
                break;
 | 
			
		||||
            case GIF:
 | 
			
		||||
                captcha = new GifCaptcha(loginCode.getWidth(), loginCode.getHeight());
 | 
			
		||||
                captcha.setLen(loginCode.getLength());
 | 
			
		||||
                captcha = new GifCaptcha(width, height);
 | 
			
		||||
                captcha.setLen(length);
 | 
			
		||||
                break;
 | 
			
		||||
            case SPEC:
 | 
			
		||||
                captcha = new SpecCaptcha(loginCode.getWidth(), loginCode.getHeight());
 | 
			
		||||
                captcha.setLen(loginCode.getLength());
 | 
			
		||||
                captcha = new SpecCaptcha(width, height);
 | 
			
		||||
                captcha.setLen(length);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw new BadConfigurationException("验证码配置信息错误!正确配置查看 LoginCodeEnum ");
 | 
			
		||||
                throw new BadRequestException("验证码配置信息错误!正确配置查看 LoginCodeEnum ");
 | 
			
		||||
        }
 | 
			
		||||
        if(StringUtils.isNotBlank(loginCode.getFontName())){
 | 
			
		||||
            captcha.setFont(new Font(loginCode.getFontName(), Font.PLAIN, loginCode.getFontSize()));
 | 
			
		||||
        if(StringUtils.isNotBlank(fontName)){
 | 
			
		||||
            captcha.setFont(new Font(fontName, Font.PLAIN, fontSize));
 | 
			
		||||
        }
 | 
			
		||||
        return captcha;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,43 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2019-2020 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package me.zhengjie.modules.security.config;
 | 
			
		||||
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.LoginProperties;
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.SecurityProperties;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @apiNote 配置文件转换Pojo类的 统一配置 类
 | 
			
		||||
 * @author: liaojinlong
 | 
			
		||||
 * @date: 2020/6/10 19:04
 | 
			
		||||
 */
 | 
			
		||||
@Configuration
 | 
			
		||||
public class ConfigBeanConfiguration {
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    @ConfigurationProperties(prefix = "login")
 | 
			
		||||
    public LoginProperties loginProperties() {
 | 
			
		||||
        return new LoginProperties();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    @ConfigurationProperties(prefix = "jwt")
 | 
			
		||||
    public SecurityProperties securityProperties() {
 | 
			
		||||
        return new SecurityProperties();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2019-2020 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version loginCode.length.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-loginCode.length.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package me.zhengjie.modules.security.config;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 配置文件读取
 | 
			
		||||
 *
 | 
			
		||||
 * @author liaojinlong
 | 
			
		||||
 * @date loginCode.length0loginCode.length0/6/10 17:loginCode.length6
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Configuration
 | 
			
		||||
@ConfigurationProperties(prefix = "login")
 | 
			
		||||
public class LoginProperties {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 账号单用户 登录
 | 
			
		||||
     */
 | 
			
		||||
    private boolean singleLogin = false;
 | 
			
		||||
 | 
			
		||||
    public static final String cacheKey = "user-login-cache:";
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -13,9 +13,11 @@
 | 
			
		|||
 *  See the License for the specific language governing permissions and
 | 
			
		||||
 *  limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package me.zhengjie.modules.security.config.bean;
 | 
			
		||||
package me.zhengjie.modules.security.config;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Jwt参数配置
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +26,8 @@ import lombok.Data;
 | 
			
		|||
 * @date 2019年11月28日
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Configuration
 | 
			
		||||
@ConfigurationProperties(prefix = "jwt")
 | 
			
		||||
public class SecurityProperties {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +16,6 @@
 | 
			
		|||
package me.zhengjie.modules.security.config;
 | 
			
		||||
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.security.*;
 | 
			
		||||
import me.zhengjie.modules.security.service.OnlineUserService;
 | 
			
		||||
import me.zhengjie.modules.security.service.UserCacheManager;
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +33,6 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
 | 
			
		|||
import org.springframework.security.config.http.SessionCreationPolicy;
 | 
			
		||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 | 
			
		||||
import org.springframework.security.crypto.password.PasswordEncoder;
 | 
			
		||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 | 
			
		||||
import org.springframework.web.filter.CorsFilter;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +52,6 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
 | 
			
		|||
    private final ApplicationContext applicationContext;
 | 
			
		||||
    private final SecurityProperties properties;
 | 
			
		||||
    private final OnlineUserService onlineUserService;
 | 
			
		||||
    private final UserCacheManager userCacheManager;
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    GrantedAuthorityDefaults grantedAuthorityDefaults() {
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +72,7 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
 | 
			
		|||
        httpSecurity
 | 
			
		||||
                // 禁用 CSRF
 | 
			
		||||
                .csrf().disable()
 | 
			
		||||
                .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
 | 
			
		||||
                .addFilter(corsFilter)
 | 
			
		||||
                // 授权异常
 | 
			
		||||
                .exceptionHandling()
 | 
			
		||||
                .authenticationEntryPoint(authenticationErrorHandler)
 | 
			
		||||
| 
						 | 
				
			
			@ -131,6 +128,6 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    private TokenConfigurer securityConfigurerAdapter() {
 | 
			
		||||
        return new TokenConfigurer(tokenProvider, properties, onlineUserService, userCacheManager);
 | 
			
		||||
        return new TokenConfigurer(tokenProvider, properties, onlineUserService);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,61 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2019-2020 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package me.zhengjie.modules.security.config.bean;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 登录验证码配置信息
 | 
			
		||||
 *
 | 
			
		||||
 * @author liaojinlong
 | 
			
		||||
 * @date 2020/6/10 18:53
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
public class LoginCode {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码配置
 | 
			
		||||
     */
 | 
			
		||||
    private LoginCodeEnum codeType;
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码有效期 分钟
 | 
			
		||||
     */
 | 
			
		||||
    private Long expiration = 2L;
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码内容长度
 | 
			
		||||
     */
 | 
			
		||||
    private int length = 2;
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码宽度
 | 
			
		||||
     */
 | 
			
		||||
    private int width = 111;
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码高度
 | 
			
		||||
     */
 | 
			
		||||
    private int height = 36;
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码字体
 | 
			
		||||
     */
 | 
			
		||||
    private String fontName;
 | 
			
		||||
    /**
 | 
			
		||||
     * 字体大小
 | 
			
		||||
     */
 | 
			
		||||
    private int fontSize = 25;
 | 
			
		||||
 | 
			
		||||
    public LoginCodeEnum getCodeType() {
 | 
			
		||||
        return codeType;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +13,7 @@
 | 
			
		|||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package me.zhengjie.modules.security.config.bean;
 | 
			
		||||
package me.zhengjie.modules.security.config.enums;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 验证码配置枚举
 | 
			
		||||
| 
						 | 
				
			
			@ -27,10 +27,12 @@ import me.zhengjie.annotation.rest.AnonymousGetMapping;
 | 
			
		|||
import me.zhengjie.annotation.rest.AnonymousPostMapping;
 | 
			
		||||
import me.zhengjie.config.properties.RsaProperties;
 | 
			
		||||
import me.zhengjie.exception.BadRequestException;
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.LoginCodeEnum;
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.LoginProperties;
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.config.CaptchaConfig;
 | 
			
		||||
import me.zhengjie.modules.security.config.enums.LoginCodeEnum;
 | 
			
		||||
import me.zhengjie.modules.security.config.LoginProperties;
 | 
			
		||||
import me.zhengjie.modules.security.config.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.security.TokenProvider;
 | 
			
		||||
import me.zhengjie.modules.security.service.UserDetailsServiceImpl;
 | 
			
		||||
import me.zhengjie.modules.security.service.dto.AuthUserDto;
 | 
			
		||||
import me.zhengjie.modules.security.service.dto.JwtUserDto;
 | 
			
		||||
import me.zhengjie.modules.security.service.OnlineUserService;
 | 
			
		||||
| 
						 | 
				
			
			@ -41,13 +43,12 @@ import me.zhengjie.utils.StringUtils;
 | 
			
		|||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.http.ResponseEntity;
 | 
			
		||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 | 
			
		||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
import org.springframework.security.core.context.SecurityContextHolder;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetails;
 | 
			
		||||
import org.springframework.security.crypto.password.PasswordEncoder;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
| 
						 | 
				
			
			@ -63,14 +64,15 @@ import java.util.concurrent.TimeUnit;
 | 
			
		|||
@RequestMapping("/auth")
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@Api(tags = "系统:系统授权接口")
 | 
			
		||||
public class AuthorizationController {
 | 
			
		||||
public class AuthController {
 | 
			
		||||
    private final SecurityProperties properties;
 | 
			
		||||
    private final RedisUtils redisUtils;
 | 
			
		||||
    private final OnlineUserService onlineUserService;
 | 
			
		||||
    private final TokenProvider tokenProvider;
 | 
			
		||||
    private final AuthenticationManagerBuilder authenticationManagerBuilder;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private LoginProperties loginProperties;
 | 
			
		||||
    private final LoginProperties loginProperties;
 | 
			
		||||
    private final CaptchaConfig captchaConfig;
 | 
			
		||||
    private final PasswordEncoder passwordEncoder;
 | 
			
		||||
    private final UserDetailsServiceImpl userDetailsService;
 | 
			
		||||
 | 
			
		||||
    @Log("用户登录")
 | 
			
		||||
    @ApiOperation("登录授权")
 | 
			
		||||
| 
						 | 
				
			
			@ -88,27 +90,29 @@ public class AuthorizationController {
 | 
			
		|||
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
 | 
			
		||||
            throw new BadRequestException("验证码错误");
 | 
			
		||||
        }
 | 
			
		||||
        UsernamePasswordAuthenticationToken authenticationToken =
 | 
			
		||||
                new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
 | 
			
		||||
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
 | 
			
		||||
        // 获取用户信息
 | 
			
		||||
        JwtUserDto jwtUser = userDetailsService.loadUserByUsername(authUser.getUsername());
 | 
			
		||||
        // 验证用户密码
 | 
			
		||||
        if (!passwordEncoder.matches(password, jwtUser.getPassword())) {
 | 
			
		||||
            throw new BadRequestException("登录密码错误");
 | 
			
		||||
        }
 | 
			
		||||
        Authentication authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities());
 | 
			
		||||
        SecurityContextHolder.getContext().setAuthentication(authentication);
 | 
			
		||||
        // 生成令牌与第三方系统获取令牌方式
 | 
			
		||||
        // UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getUsername());
 | 
			
		||||
        // Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
 | 
			
		||||
        // SecurityContextHolder.getContext().setAuthentication(authentication);
 | 
			
		||||
        String token = tokenProvider.createToken(authentication);
 | 
			
		||||
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
 | 
			
		||||
        // 生成令牌
 | 
			
		||||
        String token = tokenProvider.createToken(jwtUser);
 | 
			
		||||
        // 将密码设置为空
 | 
			
		||||
        jwtUser.setPassword(null);
 | 
			
		||||
        // 返回 token 与 用户信息
 | 
			
		||||
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
 | 
			
		||||
            put("token", properties.getTokenStartWith() + token);
 | 
			
		||||
            put("user", jwtUserDto);
 | 
			
		||||
            put("user", jwtUser);
 | 
			
		||||
        }};
 | 
			
		||||
        if (loginProperties.isSingleLogin()) {
 | 
			
		||||
            // 踢掉之前已经登录的token
 | 
			
		||||
            onlineUserService.kickOutForUsername(authUser.getUsername());
 | 
			
		||||
        }
 | 
			
		||||
        // 保存在线信息
 | 
			
		||||
        onlineUserService.save(jwtUserDto, token, request);
 | 
			
		||||
        onlineUserService.save(jwtUser, token, request);
 | 
			
		||||
        // 返回登录信息
 | 
			
		||||
        return ResponseEntity.ok(authInfo);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -116,14 +120,17 @@ public class AuthorizationController {
 | 
			
		|||
    @ApiOperation("获取用户信息")
 | 
			
		||||
    @GetMapping(value = "/info")
 | 
			
		||||
    public ResponseEntity<UserDetails> getUserInfo() {
 | 
			
		||||
        return ResponseEntity.ok(SecurityUtils.getCurrentUser());
 | 
			
		||||
        JwtUserDto jwtUser = (JwtUserDto) SecurityUtils.getCurrentUser();
 | 
			
		||||
        // 将密码设置为空
 | 
			
		||||
        jwtUser.setPassword(null);
 | 
			
		||||
        return ResponseEntity.ok(jwtUser);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ApiOperation("获取验证码")
 | 
			
		||||
    @AnonymousGetMapping(value = "/code")
 | 
			
		||||
    public ResponseEntity<Object> getCode() {
 | 
			
		||||
        // 获取运算的结果
 | 
			
		||||
        Captcha captcha = loginProperties.getCaptcha();
 | 
			
		||||
        Captcha captcha = captchaConfig.getCaptcha();
 | 
			
		||||
        String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
 | 
			
		||||
        //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型
 | 
			
		||||
        String captchaValue = captcha.text();
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +138,7 @@ public class AuthorizationController {
 | 
			
		|||
            captchaValue = captchaValue.split("\\.")[0];
 | 
			
		||||
        }
 | 
			
		||||
        // 保存
 | 
			
		||||
        redisUtils.set(uuid, captchaValue, loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES);
 | 
			
		||||
        redisUtils.set(uuid, captchaValue, captchaConfig.getExpiration(), TimeUnit.MINUTES);
 | 
			
		||||
        // 验证码信息
 | 
			
		||||
        Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
 | 
			
		||||
            put("img", captcha.toBase64());
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +15,9 @@
 | 
			
		|||
 */
 | 
			
		||||
package me.zhengjie.modules.security.security;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import me.zhengjie.exception.handler.ApiError;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.security.access.AccessDeniedException;
 | 
			
		||||
import org.springframework.security.web.access.AccessDeniedHandler;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +35,10 @@ public class JwtAccessDeniedHandler implements AccessDeniedHandler {
 | 
			
		|||
   @Override
 | 
			
		||||
   public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
 | 
			
		||||
      //当用户在没有授权的情况下访问受保护的REST资源时,将调用此方法发送403 Forbidden响应
 | 
			
		||||
      response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());
 | 
			
		||||
      response.setStatus(HttpStatus.FORBIDDEN.value());
 | 
			
		||||
      response.setContentType("application/json;charset=UTF-8");
 | 
			
		||||
      ObjectMapper objectMapper = new ObjectMapper();
 | 
			
		||||
      String jsonResponse = objectMapper.writeValueAsString(ApiError.error(HttpStatus.FORBIDDEN.value(), "禁止访问,您没有权限访问此资源"));
 | 
			
		||||
      response.getWriter().write(jsonResponse);
 | 
			
		||||
   }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,10 +15,13 @@
 | 
			
		|||
 */
 | 
			
		||||
package me.zhengjie.modules.security.security;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.zhengjie.exception.handler.ApiError;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.security.core.AuthenticationException;
 | 
			
		||||
import org.springframework.security.web.AuthenticationEntryPoint;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import javax.servlet.http.HttpServletResponse;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
| 
						 | 
				
			
			@ -26,14 +29,18 @@ import java.io.IOException;
 | 
			
		|||
/**
 | 
			
		||||
 * @author Zheng Jie
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
@Component
 | 
			
		||||
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void commence(HttpServletRequest request,
 | 
			
		||||
                         HttpServletResponse response,
 | 
			
		||||
                         AuthenticationException authException) throws IOException {
 | 
			
		||||
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
 | 
			
		||||
        // 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
 | 
			
		||||
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage());
 | 
			
		||||
        int code = HttpStatus.UNAUTHORIZED.value();
 | 
			
		||||
        response.setStatus(code);
 | 
			
		||||
        response.setContentType("application/json;charset=UTF-8");
 | 
			
		||||
        ObjectMapper objectMapper = new ObjectMapper();
 | 
			
		||||
        String jsonResponse = objectMapper.writeValueAsString(ApiError.error(HttpStatus.UNAUTHORIZED.value(), "登录状态已过期,请重新登录"));
 | 
			
		||||
        response.getWriter().write(jsonResponse);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@
 | 
			
		|||
package me.zhengjie.modules.security.security;
 | 
			
		||||
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.config.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.service.OnlineUserService;
 | 
			
		||||
import me.zhengjie.modules.security.service.UserCacheManager;
 | 
			
		||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
 | 
			
		||||
| 
						 | 
				
			
			@ -33,11 +33,10 @@ public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFi
 | 
			
		|||
    private final TokenProvider tokenProvider;
 | 
			
		||||
    private final SecurityProperties properties;
 | 
			
		||||
    private final OnlineUserService onlineUserService;
 | 
			
		||||
    private final UserCacheManager userCacheManager;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void configure(HttpSecurity http) {
 | 
			
		||||
        TokenFilter customFilter = new TokenFilter(tokenProvider, properties, onlineUserService, userCacheManager);
 | 
			
		||||
        TokenFilter customFilter = new TokenFilter(tokenProvider, properties, onlineUserService);
 | 
			
		||||
        http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,9 +16,7 @@
 | 
			
		|||
package me.zhengjie.modules.security.security;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import io.jsonwebtoken.ExpiredJwtException;
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.service.UserCacheManager;
 | 
			
		||||
import me.zhengjie.modules.security.config.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.service.dto.OnlineUserDto;
 | 
			
		||||
import me.zhengjie.modules.security.service.OnlineUserService;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +31,6 @@ import javax.servlet.ServletRequest;
 | 
			
		|||
import javax.servlet.ServletResponse;
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author /
 | 
			
		||||
| 
						 | 
				
			
			@ -45,19 +42,16 @@ public class TokenFilter extends GenericFilterBean {
 | 
			
		|||
    private final TokenProvider tokenProvider;
 | 
			
		||||
    private final SecurityProperties properties;
 | 
			
		||||
    private final OnlineUserService onlineUserService;
 | 
			
		||||
    private final UserCacheManager userCacheManager;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param tokenProvider     Token
 | 
			
		||||
     * @param properties        JWT
 | 
			
		||||
     * @param onlineUserService 用户在线
 | 
			
		||||
     * @param userCacheManager    用户缓存工具
 | 
			
		||||
     */
 | 
			
		||||
    public TokenFilter(TokenProvider tokenProvider, SecurityProperties properties, OnlineUserService onlineUserService, UserCacheManager userCacheManager) {
 | 
			
		||||
    public TokenFilter(TokenProvider tokenProvider, SecurityProperties properties, OnlineUserService onlineUserService) {
 | 
			
		||||
        this.properties = properties;
 | 
			
		||||
        this.onlineUserService = onlineUserService;
 | 
			
		||||
        this.tokenProvider = tokenProvider;
 | 
			
		||||
        this.userCacheManager = userCacheManager;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -66,25 +60,17 @@ public class TokenFilter extends GenericFilterBean {
 | 
			
		|||
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
 | 
			
		||||
        String token = resolveToken(httpServletRequest);
 | 
			
		||||
        // 对于 Token 为空的不需要去查 Redis
 | 
			
		||||
        if (StrUtil.isNotBlank(token)) {
 | 
			
		||||
            OnlineUserDto onlineUserDto = null;
 | 
			
		||||
            boolean cleanUserCache = false;
 | 
			
		||||
            try {
 | 
			
		||||
                String loginKey = tokenProvider.loginKey(token);
 | 
			
		||||
                onlineUserDto = onlineUserService.getOne(loginKey);
 | 
			
		||||
            } catch (ExpiredJwtException e) {
 | 
			
		||||
                log.error(e.getMessage());
 | 
			
		||||
                cleanUserCache = true;
 | 
			
		||||
            } finally {
 | 
			
		||||
                if (cleanUserCache || Objects.isNull(onlineUserDto)) {
 | 
			
		||||
                    userCacheManager.cleanUserCache(String.valueOf(tokenProvider.getClaims(token).get(TokenProvider.AUTHORITIES_KEY)));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (onlineUserDto != null && StringUtils.hasText(token)) {
 | 
			
		||||
        if(StrUtil.isNotBlank(token)){
 | 
			
		||||
            // 获取用户Token的Key
 | 
			
		||||
            String loginKey = tokenProvider.loginKey(token);
 | 
			
		||||
            OnlineUserDto onlineUserDto = onlineUserService.getOne(loginKey);
 | 
			
		||||
            // 判断用户在线信息是否为空
 | 
			
		||||
            if (onlineUserDto != null) {
 | 
			
		||||
                // Token 续期判断
 | 
			
		||||
                tokenProvider.checkRenewal(token);
 | 
			
		||||
                // 获取认证信息,设置上下文
 | 
			
		||||
                Authentication authentication = tokenProvider.getAuthentication(token);
 | 
			
		||||
                SecurityContextHolder.getContext().setAuthentication(authentication);
 | 
			
		||||
                // Token 续期
 | 
			
		||||
                tokenProvider.checkRenewal(token);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        filterChain.doFilter(servletRequest, servletResponse);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,8 @@ import io.jsonwebtoken.*;
 | 
			
		|||
import io.jsonwebtoken.io.Decoders;
 | 
			
		||||
import io.jsonwebtoken.security.Keys;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.config.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.service.dto.JwtUserDto;
 | 
			
		||||
import me.zhengjie.utils.RedisUtils;
 | 
			
		||||
import org.springframework.beans.factory.InitializingBean;
 | 
			
		||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 | 
			
		||||
| 
						 | 
				
			
			@ -42,11 +43,12 @@ import java.util.concurrent.TimeUnit;
 | 
			
		|||
@Component
 | 
			
		||||
public class TokenProvider implements InitializingBean {
 | 
			
		||||
 | 
			
		||||
    private final SecurityProperties properties;
 | 
			
		||||
    private final RedisUtils redisUtils;
 | 
			
		||||
    public static final String AUTHORITIES_KEY = "user";
 | 
			
		||||
    private JwtParser jwtParser;
 | 
			
		||||
    private JwtBuilder jwtBuilder;
 | 
			
		||||
    private final RedisUtils redisUtils;
 | 
			
		||||
    private final SecurityProperties properties;
 | 
			
		||||
    public static final String AUTHORITIES_UUID_KEY = "uuid";
 | 
			
		||||
    public static final String AUTHORITIES_UID_KEY = "userId";
 | 
			
		||||
 | 
			
		||||
    public TokenProvider(SecurityProperties properties, RedisUtils redisUtils) {
 | 
			
		||||
        this.properties = properties;
 | 
			
		||||
| 
						 | 
				
			
			@ -68,15 +70,19 @@ public class TokenProvider implements InitializingBean {
 | 
			
		|||
     * 创建Token 设置永不过期,
 | 
			
		||||
     * Token 的时间有效性转到Redis 维护
 | 
			
		||||
     *
 | 
			
		||||
     * @param authentication /
 | 
			
		||||
     * @param user /
 | 
			
		||||
     * @return /
 | 
			
		||||
     */
 | 
			
		||||
    public String createToken(Authentication authentication) {
 | 
			
		||||
    public String createToken(JwtUserDto user) {
 | 
			
		||||
        // 设置参数
 | 
			
		||||
        Map<String, Object> claims = new HashMap<>(6);
 | 
			
		||||
        // 设置用户ID
 | 
			
		||||
        claims.put(AUTHORITIES_UID_KEY, user.getUser().getId());
 | 
			
		||||
        // 设置UUID,确保每次Token不一样
 | 
			
		||||
        claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
 | 
			
		||||
        return jwtBuilder
 | 
			
		||||
                // 加入ID确保生成的 Token 都不一致
 | 
			
		||||
                .setId(IdUtil.simpleUUID())
 | 
			
		||||
                .claim(AUTHORITIES_KEY, authentication.getName())
 | 
			
		||||
                .setSubject(authentication.getName())
 | 
			
		||||
                .setClaims(claims)
 | 
			
		||||
                .setSubject(user.getUsername())
 | 
			
		||||
                .compact();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,12 +19,11 @@ import lombok.AllArgsConstructor;
 | 
			
		|||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.zhengjie.modules.security.security.TokenProvider;
 | 
			
		||||
import me.zhengjie.utils.PageResult;
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.config.SecurityProperties;
 | 
			
		||||
import me.zhengjie.modules.security.service.dto.JwtUserDto;
 | 
			
		||||
import me.zhengjie.modules.security.service.dto.OnlineUserDto;
 | 
			
		||||
import me.zhengjie.utils.*;
 | 
			
		||||
import org.springframework.data.domain.Pageable;
 | 
			
		||||
import org.springframework.scheduling.annotation.Async;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import javax.servlet.http.HttpServletResponse;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@
 | 
			
		|||
package me.zhengjie.modules.security.service;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.RandomUtil;
 | 
			
		||||
import me.zhengjie.modules.security.config.bean.LoginProperties;
 | 
			
		||||
import me.zhengjie.modules.security.config.LoginProperties;
 | 
			
		||||
import me.zhengjie.modules.security.service.dto.JwtUserDto;
 | 
			
		||||
import me.zhengjie.utils.RedisUtils;
 | 
			
		||||
import me.zhengjie.utils.StringUtils;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,14 +18,12 @@ package me.zhengjie.modules.security.service;
 | 
			
		|||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import me.zhengjie.exception.BadRequestException;
 | 
			
		||||
import me.zhengjie.exception.EntityNotFoundException;
 | 
			
		||||
import me.zhengjie.modules.security.service.dto.JwtUserDto;
 | 
			
		||||
import me.zhengjie.modules.system.service.DataService;
 | 
			
		||||
import me.zhengjie.modules.system.service.RoleService;
 | 
			
		||||
import me.zhengjie.modules.system.service.UserService;
 | 
			
		||||
import me.zhengjie.modules.system.service.dto.UserLoginDto;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetailsService;
 | 
			
		||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -45,24 +43,14 @@ public class UserDetailsServiceImpl implements UserDetailsService {
 | 
			
		|||
    public JwtUserDto loadUserByUsername(String username) {
 | 
			
		||||
        JwtUserDto jwtUserDto = userCacheManager.getUserCache(username);
 | 
			
		||||
        if(jwtUserDto == null){
 | 
			
		||||
            UserLoginDto user;
 | 
			
		||||
            try {
 | 
			
		||||
                user = userService.getLoginData(username);
 | 
			
		||||
            } catch (EntityNotFoundException e) {
 | 
			
		||||
                // SpringSecurity会自动转换UsernameNotFoundException为BadCredentialsException
 | 
			
		||||
                throw new UsernameNotFoundException(username, e);
 | 
			
		||||
            }
 | 
			
		||||
            UserLoginDto user = userService.getLoginData(username);
 | 
			
		||||
            if (user == null) {
 | 
			
		||||
                throw new UsernameNotFoundException("");
 | 
			
		||||
                throw new BadRequestException("用户不存在");
 | 
			
		||||
            } else {
 | 
			
		||||
                if (!user.getEnabled()) {
 | 
			
		||||
                    throw new BadRequestException("账号未激活!");
 | 
			
		||||
                }
 | 
			
		||||
                jwtUserDto = new JwtUserDto(
 | 
			
		||||
                        user,
 | 
			
		||||
                        dataService.getDeptIds(user),
 | 
			
		||||
                        roleService.mapToGrantedAuthorities(user)
 | 
			
		||||
                );
 | 
			
		||||
                jwtUserDto = new JwtUserDto(user, dataService.getDeptIds(user), roleService.buildAuthorities(user), user.getPassword());
 | 
			
		||||
                // 添加缓存数据
 | 
			
		||||
                userCacheManager.addUserCache(username, jwtUserDto);
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,8 +16,10 @@
 | 
			
		|||
package me.zhengjie.modules.security.service.dto;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.fastjson.annotation.JSONField;
 | 
			
		||||
import io.swagger.annotations.ApiModelProperty;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import me.zhengjie.modules.system.service.dto.UserLoginDto;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetails;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +40,10 @@ public class JwtUserDto implements UserDetails {
 | 
			
		|||
 | 
			
		||||
    private final List<AuthorityDto> authorities;
 | 
			
		||||
 | 
			
		||||
    @Setter
 | 
			
		||||
    @ApiModelProperty(value = "密码")
 | 
			
		||||
    private String password;
 | 
			
		||||
 | 
			
		||||
    public Set<String> getRoles() {
 | 
			
		||||
        return authorities.stream().map(AuthorityDto::getAuthority).collect(Collectors.toSet());
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,7 +120,7 @@ public interface RoleService {
 | 
			
		|||
     * @param user 用户信息
 | 
			
		||||
     * @return 权限信息
 | 
			
		||||
     */
 | 
			
		||||
    List<AuthorityDto> mapToGrantedAuthorities(UserDto user);
 | 
			
		||||
    List<AuthorityDto> buildAuthorities(UserDto user);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证是否被用户关联
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,8 @@
 | 
			
		|||
 */
 | 
			
		||||
package me.zhengjie.modules.system.service.dto;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.fastjson.annotation.JSONField;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Zheng Jie
 | 
			
		||||
 * @description 用户缓存时使用
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +24,7 @@ package me.zhengjie.modules.system.service.dto;
 | 
			
		|||
 **/
 | 
			
		||||
public class UserLoginDto extends UserDto {
 | 
			
		||||
 | 
			
		||||
    @JSONField(serialize = false)
 | 
			
		||||
    private String password;
 | 
			
		||||
 | 
			
		||||
    private Boolean isAdmin;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -164,7 +164,7 @@ public class RoleServiceImpl implements RoleService {
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Cacheable(key = "'auth:' + #p0.id")
 | 
			
		||||
    public List<AuthorityDto> mapToGrantedAuthorities(UserDto user) {
 | 
			
		||||
    public List<AuthorityDto> buildAuthorities(UserDto user) {
 | 
			
		||||
        Set<String> permissions = new HashSet<>();
 | 
			
		||||
        // 如果是管理员直接返回
 | 
			
		||||
        if (user.getIsAdmin()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,7 +60,7 @@ login:
 | 
			
		|||
    # 存活时间/秒
 | 
			
		||||
    idle-time: 21600
 | 
			
		||||
  #  验证码
 | 
			
		||||
  login-code:
 | 
			
		||||
  code:
 | 
			
		||||
    #  验证码类型配置 查看 LoginProperties 类
 | 
			
		||||
    code-type: arithmetic
 | 
			
		||||
    #  登录图形验证码有效时间/分钟
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,7 +64,7 @@ login:
 | 
			
		|||
    # 存活时间/秒
 | 
			
		||||
    idle-time: 21600
 | 
			
		||||
  #  验证码
 | 
			
		||||
  login-code:
 | 
			
		||||
  code:
 | 
			
		||||
    #  验证码类型配置 查看 LoginProperties 类
 | 
			
		||||
    code-type: arithmetic
 | 
			
		||||
    #  登录图形验证码有效时间/分钟
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue