refactor: 优化安全配置和代码结构

This commit is contained in:
Jie Zheng 2025-01-13 16:46:48 +08:00
parent bb9a0d7213
commit d2bb69798a
18 changed files with 100 additions and 76 deletions

View File

@ -26,31 +26,38 @@ public interface CacheKey {
* 用户 * 用户
*/ */
String USER_ID = "user::id:"; String USER_ID = "user::id:";
/** /**
* 数据 * 数据
*/ */
String DATA_USER = "data::user:"; String DATA_USER = "data::user:";
/** /**
* 菜单 * 菜单
*/ */
String MENU_ID = "menu::id:"; String MENU_ID = "menu::id:";
String MENU_USER = "menu::user:"; String MENU_USER = "menu::user:";
/** /**
* 角色授权 * 角色授权
*/ */
String ROLE_AUTH = "role::auth:"; String ROLE_AUTH = "role::auth:";
/** /**
* 角色信息 * 角色信息
*/ */
String ROLE_ID = "role::id:"; String ROLE_ID = "role::id:";
/** /**
* 部门 * 部门
*/ */
String DEPT_ID = "dept::id:"; String DEPT_ID = "dept::id:";
/** /**
* 岗位 * 岗位
*/ */
String JOB_ID = "job::id:"; String JOB_ID = "job::id:";
/** /**
* 数据字典 * 数据字典
*/ */

View File

@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.*;
import com.wf.captcha.base.Captcha; import com.wf.captcha.base.Captcha;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import me.zhengjie.exception.BadRequestException; import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.security.config.enums.LoginCodeEnum;
import me.zhengjie.utils.StringUtils; import me.zhengjie.utils.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -33,7 +34,7 @@ import java.awt.*;
@Data @Data
@Configuration @Configuration
@ConfigurationProperties(prefix = "login.code") @ConfigurationProperties(prefix = "login.code")
public class CodeProperties { public class CaptchaConfig {
/** /**
* 验证码配置 * 验证码配置

View File

@ -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();
}
}

View File

@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package me.zhengjie.modules.security.config.bean; package me.zhengjie.modules.security.config;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/** /**
* 配置文件读取 * 配置文件读取
@ -24,6 +26,8 @@ import lombok.Data;
* @date loginCode.length0loginCode.length0/6/10 17:loginCode.length6 * @date loginCode.length0loginCode.length0/6/10 17:loginCode.length6
*/ */
@Data @Data
@Configuration
@ConfigurationProperties(prefix = "login")
public class LoginProperties { public class LoginProperties {
/** /**
@ -31,5 +35,5 @@ public class LoginProperties {
*/ */
private boolean singleLogin = false; private boolean singleLogin = false;
public static final String cacheKey = "user-login-cache:"; public static final String cacheKey = "user_login_cache:";
} }

View File

@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package me.zhengjie.modules.security.config.bean; package me.zhengjie.modules.security.config;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/** /**
* Jwt参数配置 * Jwt参数配置
@ -24,6 +26,8 @@ import lombok.Data;
* @date 2019年11月28日 * @date 2019年11月28日
*/ */
@Data @Data
@Configuration
@ConfigurationProperties(prefix = "jwt")
public class SecurityProperties { public class SecurityProperties {
/** /**

View File

@ -16,7 +16,6 @@
package me.zhengjie.modules.security.config; package me.zhengjie.modules.security.config;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.security.config.bean.SecurityProperties;
import me.zhengjie.modules.security.security.*; import me.zhengjie.modules.security.security.*;
import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.OnlineUserService;
import me.zhengjie.modules.security.service.UserCacheManager; import me.zhengjie.modules.security.service.UserCacheManager;

View File

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package me.zhengjie.modules.security.config.bean; package me.zhengjie.modules.security.config.enums;
/** /**
* 验证码配置枚举 * 验证码配置枚举
* *
* @author: liaojinlong * @author liaojinlong
* @date: 2020/6/10 17:40 * @date 2020/6/10 17:40
*/ */
public enum LoginCodeEnum { public enum LoginCodeEnum {
@ -39,5 +39,8 @@ public enum LoginCodeEnum {
* 闪图 * 闪图
*/ */
GIF, GIF,
/**
* 静态
*/
SPEC SPEC
} }

View File

@ -27,10 +27,10 @@ import me.zhengjie.annotation.rest.AnonymousGetMapping;
import me.zhengjie.annotation.rest.AnonymousPostMapping; import me.zhengjie.annotation.rest.AnonymousPostMapping;
import me.zhengjie.config.properties.RsaProperties; import me.zhengjie.config.properties.RsaProperties;
import me.zhengjie.exception.BadRequestException; import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.security.config.bean.CodeProperties; import me.zhengjie.modules.security.config.CaptchaConfig;
import me.zhengjie.modules.security.config.bean.LoginCodeEnum; import me.zhengjie.modules.security.config.enums.LoginCodeEnum;
import me.zhengjie.modules.security.config.bean.LoginProperties; import me.zhengjie.modules.security.config.LoginProperties;
import me.zhengjie.modules.security.config.bean.SecurityProperties; import me.zhengjie.modules.security.config.SecurityProperties;
import me.zhengjie.modules.security.security.TokenProvider; import me.zhengjie.modules.security.security.TokenProvider;
import me.zhengjie.modules.security.service.dto.AuthUserDto; import me.zhengjie.modules.security.service.dto.AuthUserDto;
import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.modules.security.service.dto.JwtUserDto;
@ -70,7 +70,7 @@ public class AuthController {
private final OnlineUserService onlineUserService; private final OnlineUserService onlineUserService;
private final TokenProvider tokenProvider; private final TokenProvider tokenProvider;
private final AuthenticationManagerBuilder authenticationManagerBuilder; private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final CodeProperties codeProperties; private final CaptchaConfig captchaConfig;
private final LoginProperties loginProperties; private final LoginProperties loginProperties;
@Log("用户登录") @Log("用户登录")
@ -124,7 +124,7 @@ public class AuthController {
@AnonymousGetMapping(value = "/code") @AnonymousGetMapping(value = "/code")
public ResponseEntity<Object> getCode() { public ResponseEntity<Object> getCode() {
// 获取运算的结果 // 获取运算的结果
Captcha captcha = codeProperties.getCaptcha(); Captcha captcha = captchaConfig.getCaptcha();
String uuid = properties.getCodeKey() + IdUtil.simpleUUID(); String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
//当验证码类型为 arithmetic时且长度 >= 2 captcha.text()的结果有几率为浮点型 //当验证码类型为 arithmetic时且长度 >= 2 captcha.text()的结果有几率为浮点型
String captchaValue = captcha.text(); String captchaValue = captcha.text();
@ -132,7 +132,7 @@ public class AuthController {
captchaValue = captchaValue.split("\\.")[0]; captchaValue = captchaValue.split("\\.")[0];
} }
// 保存 // 保存
redisUtils.set(uuid, captchaValue, codeProperties.getExpiration(), TimeUnit.MINUTES); redisUtils.set(uuid, captchaValue, captchaConfig.getExpiration(), TimeUnit.MINUTES);
// 验证码信息 // 验证码信息
Map<String, Object> imgResult = new HashMap<String, Object>(2) {{ Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
put("img", captcha.toBase64()); put("img", captcha.toBase64());

View File

@ -15,10 +15,12 @@
*/ */
package me.zhengjie.modules.security.security; 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.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
@ -32,6 +34,10 @@ public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override @Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
//当用户在没有授权的情况下访问受保护的REST资源时将调用此方法发送403 Forbidden响应 //当用户在没有授权的情况下访问受保护的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);
} }
} }

View File

@ -15,10 +15,13 @@
*/ */
package me.zhengjie.modules.security.security; 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.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
@ -26,14 +29,18 @@ import java.io.IOException;
/** /**
* @author Zheng Jie * @author Zheng Jie
*/ */
@Slf4j
@Component @Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override @Override
public void commence(HttpServletRequest request, public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
HttpServletResponse response,
AuthenticationException authException) throws IOException {
// 当用户尝试访问安全的REST资源而不提供任何凭据时将调用此方法发送401 响应 // 当用户尝试访问安全的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);
} }
} }

View File

@ -16,7 +16,7 @@
package me.zhengjie.modules.security.security; package me.zhengjie.modules.security.security;
import lombok.RequiredArgsConstructor; 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.OnlineUserService;
import me.zhengjie.modules.security.service.UserCacheManager; import me.zhengjie.modules.security.service.UserCacheManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.SecurityConfigurerAdapter;

View File

@ -17,7 +17,7 @@ package me.zhengjie.modules.security.security;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.ExpiredJwtException;
import me.zhengjie.modules.security.config.bean.SecurityProperties; import me.zhengjie.modules.security.config.SecurityProperties;
import me.zhengjie.modules.security.service.UserCacheManager; import me.zhengjie.modules.security.service.UserCacheManager;
import me.zhengjie.modules.security.service.dto.OnlineUserDto; import me.zhengjie.modules.security.service.dto.OnlineUserDto;
import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.OnlineUserService;

View File

@ -23,7 +23,7 @@ import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhengjie.modules.security.config.bean.SecurityProperties; import me.zhengjie.modules.security.config.SecurityProperties;
import me.zhengjie.utils.RedisUtils; import me.zhengjie.utils.RedisUtils;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

View File

@ -19,12 +19,11 @@ import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhengjie.modules.security.security.TokenProvider; import me.zhengjie.modules.security.security.TokenProvider;
import me.zhengjie.utils.PageResult; 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.JwtUserDto;
import me.zhengjie.modules.security.service.dto.OnlineUserDto; import me.zhengjie.modules.security.service.dto.OnlineUserDto;
import me.zhengjie.utils.*; import me.zhengjie.utils.*;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;

View File

@ -16,7 +16,7 @@
package me.zhengjie.modules.security.service; package me.zhengjie.modules.security.service;
import cn.hutool.core.util.RandomUtil; 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.modules.security.service.dto.JwtUserDto;
import me.zhengjie.utils.RedisUtils; import me.zhengjie.utils.RedisUtils;
import me.zhengjie.utils.StringUtils; import me.zhengjie.utils.StringUtils;

View File

@ -0,0 +1,37 @@
/*
* 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.sysrunner;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* @author Zheng Jie
* @description 程序启动后处理数据
* @date 2025-01-13
**/
@Slf4j
@Component
@RequiredArgsConstructor
public class SystemRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
}
}

View File

@ -86,9 +86,9 @@ jwt:
# 令牌过期时间 此处单位/毫秒 默认4小时可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html # 令牌过期时间 此处单位/毫秒 默认4小时可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
token-validity-in-seconds: 14400000 token-validity-in-seconds: 14400000
# 在线用户key # 在线用户key
online-key: "online-token:" online-key: "online_token:"
# 验证码 # 验证码
code-key: "captcha-code:" code-key: "captcha_code:"
# token 续期检查时间范围默认30分钟单位毫秒在token即将过期的一段时间内用户操作了则给用户的token续期 # token 续期检查时间范围默认30分钟单位毫秒在token即将过期的一段时间内用户操作了则给用户的token续期
detect: 1800000 detect: 1800000
# 续期时间范围默认1小时单位毫秒 # 续期时间范围默认1小时单位毫秒

View File

@ -90,9 +90,9 @@ jwt:
# 令牌过期时间 此处单位/毫秒 默认2小时可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html # 令牌过期时间 此处单位/毫秒 默认2小时可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
token-validity-in-seconds: 7200000 token-validity-in-seconds: 7200000
# 在线用户key # 在线用户key
online-key: "online-token:" online-key: "online_token:"
# 验证码 # 验证码
code-key: "captcha-code:" code-key: "captcha_code:"
# token 续期检查时间范围默认30分钟单位默认毫秒在token即将过期的一段时间内用户操作了则给用户的token续期 # token 续期检查时间范围默认30分钟单位默认毫秒在token即将过期的一段时间内用户操作了则给用户的token续期
detect: 1800000 detect: 1800000
# 续期时间范围,默认 1小时这里单位毫秒 # 续期时间范围,默认 1小时这里单位毫秒