feat: 添加 spring-boot-security starter.
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
package com.orion.ops.framework.common.security;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前登录用户
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/6 18:36
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class LoginUser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 花名
|
||||||
|
*/
|
||||||
|
private String nickname;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色
|
||||||
|
*/
|
||||||
|
private List<String> roles;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.orion.ops.framework.common.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SecurityUtils 的 bean 对象
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/7 15:20
|
||||||
|
*/
|
||||||
|
public interface SecurityHolder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户
|
||||||
|
*
|
||||||
|
* @return 当前用户
|
||||||
|
*/
|
||||||
|
LoginUser getLoginUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户id
|
||||||
|
*
|
||||||
|
* @return id
|
||||||
|
*/
|
||||||
|
Long getLoginUserId();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.orion.ops.framework.security.config;
|
||||||
|
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义安全策略配置
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/7 12:58
|
||||||
|
*/
|
||||||
|
public abstract class AuthorizeRequestsCustomizer
|
||||||
|
implements Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry>, Ordered {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
package com.orion.ops.framework.security.config;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy;
|
||||||
|
import com.orion.ops.framework.security.core.filter.TokenAuthenticationFilter;
|
||||||
|
import com.orion.ops.framework.security.core.handler.AuthenticationEntryPointHandler;
|
||||||
|
import com.orion.ops.framework.security.core.handler.ForbiddenAccessDeniedHandler;
|
||||||
|
import com.orion.ops.framework.security.core.service.SecurityFrameworkService;
|
||||||
|
import com.orion.ops.framework.security.core.service.SecurityFrameworkServiceDelegate;
|
||||||
|
import com.orion.ops.framework.security.core.service.SecurityHolderDelegate;
|
||||||
|
import com.orion.ops.framework.security.core.strategy.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目安全配置类
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/6 15:05
|
||||||
|
*/
|
||||||
|
@AutoConfiguration
|
||||||
|
@EnableConfigurationProperties(SecurityConfig.class)
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||||
|
public class OrionSecurityAutoConfiguration {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SecurityConfig securityConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 认证失败处理器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public AuthenticationEntryPoint authenticationEntryPoint() {
|
||||||
|
return new AuthenticationEntryPointHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 权限不足处理器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public AccessDeniedHandler accessDeniedHandler() {
|
||||||
|
return new ForbiddenAccessDeniedHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 密码加密器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder(securityConfig.getPasswordEncoderLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AuthenticationManager 不是bean
|
||||||
|
* 重写父类方法可注入 AuthenticationManager
|
||||||
|
*
|
||||||
|
* @param authenticationConfiguration configuration
|
||||||
|
* @return AuthenticationManagerBean
|
||||||
|
* @throws Exception Exception
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public AuthenticationManager authenticationManagerBean(AuthenticationConfiguration authenticationConfiguration) throws Exception {
|
||||||
|
return authenticationConfiguration.getAuthenticationManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 声明调用 {@link SecurityContextHolder#setStrategyName(String)} 方法
|
||||||
|
* 设置使用 {@link TransmittableThreadLocalSecurityContextHolderStrategy} 作为 Security 的上下文策略
|
||||||
|
*
|
||||||
|
* @return 替换策略
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public MethodInvokingFactoryBean securityContextHolderMethodInvokingFactoryBean() {
|
||||||
|
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
|
||||||
|
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
|
||||||
|
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
|
||||||
|
methodInvokingFactoryBean.setArguments(TransmittableThreadLocalSecurityContextHolderStrategy.class.getName());
|
||||||
|
return methodInvokingFactoryBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param impl impl
|
||||||
|
* @return 安全框架服务
|
||||||
|
*/
|
||||||
|
@Bean("ss")
|
||||||
|
@Primary
|
||||||
|
@ConditionalOnBean(SecurityFrameworkService.class)
|
||||||
|
public SecurityFrameworkServiceDelegate securityFrameworkService(SecurityFrameworkService impl) {
|
||||||
|
return new SecurityFrameworkServiceDelegate(impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param delegate delegate
|
||||||
|
* @return token 认证过滤器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnBean(SecurityFrameworkService.class)
|
||||||
|
public TokenAuthenticationFilter authenticationTokenFilter(SecurityFrameworkService delegate) {
|
||||||
|
return new TokenAuthenticationFilter(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return security holder 代理用于内部 framework 调用
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public SecurityHolderDelegate securityHolder() {
|
||||||
|
return new SecurityHolderDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 静态资源安全策略
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public StaticResourceAuthorizeRequestsCustomizer staticResourceAuthorizeRequestsCustomizer() {
|
||||||
|
return new StaticResourceAuthorizeRequestsCustomizer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param applicationContext applicationContext
|
||||||
|
* @return 匿名接口安全策略
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public PermitAllAnnotationAuthorizeRequestsCustomizer permitAllAnnotationAuthorizeRequestsCustomizer(ApplicationContext applicationContext) {
|
||||||
|
return new PermitAllAnnotationAuthorizeRequestsCustomizer(applicationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 配置文件安全策略
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ConfigAuthorizeRequestsCustomizer configAuthorizeRequestsCustomizer() {
|
||||||
|
return new ConfigAuthorizeRequestsCustomizer(securityConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return websocket 安全策略
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public WebsocketAuthorizeRequestsCustomizer websocketAuthorizeRequestsCustomizer() {
|
||||||
|
return new WebsocketAuthorizeRequestsCustomizer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param adminSeverContextPath adminSeverContextPath
|
||||||
|
* @return 控制台安全策略
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ConsoleAuthorizeRequestsCustomizer consoleAuthorizeRequestsCustomizer(@Value("${spring.boot.admin.context-path:''}") String adminSeverContextPath) {
|
||||||
|
return new ConsoleAuthorizeRequestsCustomizer(adminSeverContextPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置安全配置
|
||||||
|
* <p>
|
||||||
|
* anyRequest | 匹配所有请求路径
|
||||||
|
* access | SpringEl 表达式结果为 true 时可以访问
|
||||||
|
* anonymous | 匿名可以访问
|
||||||
|
* denyAll | 用户不能访问
|
||||||
|
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
|
||||||
|
* hasAnyAuthority | 如果有参数, 参数表示权限, 则其中任何一个权限可以访问
|
||||||
|
* hasAnyRole | 如果有参数, 参数表示角色, 则其中任何一个角色可以访问
|
||||||
|
* hasAuthority | 如果有参数, 参数表示权限, 则其权限可以访问
|
||||||
|
* hasIpAddress | 如果有参数, 参数表示IP地址, 如果用户IP和参数匹配, 则可以访问
|
||||||
|
* hasRole | 如果有参数, 参数表示角色, 则其角色可以访问
|
||||||
|
* permitAll | 用户可以任意访问
|
||||||
|
* rememberMe | 允许通过 remember-me 登录的用户访问
|
||||||
|
* authenticated | 用户登录后可访问
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
protected SecurityFilterChain filterChain(List<AuthorizeRequestsCustomizer> authorizeRequestsCustomizers,
|
||||||
|
AuthenticationEntryPoint authenticationEntryPoint,
|
||||||
|
AccessDeniedHandler accessDeniedHandler,
|
||||||
|
TokenAuthenticationFilter authenticationTokenFilter,
|
||||||
|
HttpSecurity httpSecurity) throws Exception {
|
||||||
|
return httpSecurity
|
||||||
|
// 开启跨域
|
||||||
|
.cors().and()
|
||||||
|
// 因为不使用session 禁用CSRF
|
||||||
|
.csrf().disable()
|
||||||
|
// 基于 token 机制所以不需要 session
|
||||||
|
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
||||||
|
// 不设置响应报头
|
||||||
|
.headers().frameOptions().disable().and()
|
||||||
|
// 认证失败处理器
|
||||||
|
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
|
||||||
|
// 权限不足处理器
|
||||||
|
.accessDeniedHandler(accessDeniedHandler).and()
|
||||||
|
// 设置请求权限策略
|
||||||
|
.authorizeRequests(registry -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(registry)))
|
||||||
|
// 兜底规则 必须认证
|
||||||
|
.authorizeRequests()
|
||||||
|
.anyRequest()
|
||||||
|
.authenticated().and()
|
||||||
|
// 在密码认证器之前添加 token 过滤器
|
||||||
|
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.orion.ops.framework.security.config;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.common.utils.ConfigUtils;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全配置
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/6 15:55
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties("orion.security")
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密复杂度
|
||||||
|
*/
|
||||||
|
private Integer passwordEncoderLength = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匿名接口
|
||||||
|
*/
|
||||||
|
private List<String> permitUrl;
|
||||||
|
|
||||||
|
public void setPermitUrl(List<String> permitUrl) {
|
||||||
|
this.permitUrl = ConfigUtils.parseStringList(permitUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.orion.ops.framework.security.core.context;
|
||||||
|
|
||||||
|
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||||
|
import com.orion.lang.utils.Valid;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||||
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 TransmittableThreadLocal 实现 Security Context 持有者策略
|
||||||
|
* 避免异步执行时 ThreadLocal 的丢失问题
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/6 15:55
|
||||||
|
*/
|
||||||
|
public class TransmittableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 TransmittableThreadLocal 作为上下文
|
||||||
|
*/
|
||||||
|
private static final ThreadLocal<SecurityContext> CONTEXT_HOLDER = new TransmittableThreadLocal<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearContext() {
|
||||||
|
CONTEXT_HOLDER.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityContext getContext() {
|
||||||
|
SecurityContext ctx = CONTEXT_HOLDER.get();
|
||||||
|
if (ctx == null) {
|
||||||
|
ctx = this.createEmptyContext();
|
||||||
|
CONTEXT_HOLDER.set(ctx);
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContext(SecurityContext context) {
|
||||||
|
Valid.notNull(context, "Only non-null SecurityContext instances are permitted");
|
||||||
|
CONTEXT_HOLDER.set(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityContext createEmptyContext() {
|
||||||
|
return new SecurityContextImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.orion.ops.framework.security.core.filter;
|
||||||
|
|
||||||
|
import com.orion.lang.utils.Strings;
|
||||||
|
import com.orion.ops.framework.common.security.LoginUser;
|
||||||
|
import com.orion.ops.framework.security.core.service.SecurityFrameworkService;
|
||||||
|
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证过滤器
|
||||||
|
* 验证 token 有效后将其加入上下文
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/6 18:39
|
||||||
|
*/
|
||||||
|
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final SecurityFrameworkService securityFrameworkService;
|
||||||
|
|
||||||
|
public TokenAuthenticationFilter(SecurityFrameworkService securityFrameworkService) {
|
||||||
|
this.securityFrameworkService = securityFrameworkService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
|
||||||
|
// 获取请求头 token
|
||||||
|
String token = SecurityUtils.obtainAuthorization(request);
|
||||||
|
if (!Strings.isBlank(token)) {
|
||||||
|
// 通过 token 获取用户信息
|
||||||
|
LoginUser loginUser = securityFrameworkService.getUserByToken(token);
|
||||||
|
// 设置上下文
|
||||||
|
if (loginUser != null) {
|
||||||
|
SecurityUtils.setLoginUser(loginUser, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// todo 全局异常返回 mock模式
|
||||||
|
// 继续执行
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.orion.ops.framework.security.core.handler;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.common.constant.ErrorCode;
|
||||||
|
import com.orion.web.servlet.web.Servlets;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证失败处理器
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/6 16:01
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class AuthenticationEntryPointHandler implements AuthenticationEntryPoint {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
|
||||||
|
log.debug("AuthenticationEntryPoint-commence-未登录 {}", request.getRequestURI(), e);
|
||||||
|
Servlets.writeHttpWrapper(response, ErrorCode.UNAUTHORIZED.getWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.orion.ops.framework.security.core.handler;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.common.constant.ErrorCode;
|
||||||
|
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||||
|
import com.orion.web.servlet.web.Servlets;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限不足处理器
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/6 16:01
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ForbiddenAccessDeniedHandler implements AccessDeniedHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException {
|
||||||
|
log.warn("AccessDeniedHandlerImpl-handle-无权限 {} {}", SecurityUtils.getLoginUserId(), request.getRequestURI());
|
||||||
|
Servlets.writeHttpWrapper(response, ErrorCode.FORBIDDEN.getWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.orion.ops.framework.security.core.service;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.common.security.LoginUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验服务
|
||||||
|
* <p>
|
||||||
|
* 在业务层定义 bean
|
||||||
|
* 使用 @PreAuthorize("@ss.hasPermission('xxx')")
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/6 18:25
|
||||||
|
*/
|
||||||
|
public interface SecurityFrameworkService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有权限
|
||||||
|
*
|
||||||
|
* @param permission 权限
|
||||||
|
* @return has
|
||||||
|
*/
|
||||||
|
boolean hasPermission(String permission);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有角色
|
||||||
|
*
|
||||||
|
* @param role 角色
|
||||||
|
* @return has
|
||||||
|
*/
|
||||||
|
boolean hasRole(String role);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 token 获取用户信息
|
||||||
|
*
|
||||||
|
* @param token token
|
||||||
|
* @return user
|
||||||
|
*/
|
||||||
|
LoginUser getUserByToken(String token);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.orion.ops.framework.security.core.service;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.common.security.LoginUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验服务委托类
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/7 11:02
|
||||||
|
*/
|
||||||
|
public class SecurityFrameworkServiceDelegate implements SecurityFrameworkService {
|
||||||
|
|
||||||
|
private final SecurityFrameworkService delegate;
|
||||||
|
|
||||||
|
public SecurityFrameworkServiceDelegate(SecurityFrameworkService delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(String permission) {
|
||||||
|
return delegate.hasPermission(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasRole(String role) {
|
||||||
|
return delegate.hasRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginUser getUserByToken(String token) {
|
||||||
|
return delegate.getUserByToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.orion.ops.framework.security.core.service;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.common.security.LoginUser;
|
||||||
|
import com.orion.ops.framework.common.security.SecurityHolder;
|
||||||
|
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SecurityHolder 委托类
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/7 15:33
|
||||||
|
*/
|
||||||
|
public class SecurityHolderDelegate implements SecurityHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginUser getLoginUser() {
|
||||||
|
return SecurityUtils.getLoginUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getLoginUserId() {
|
||||||
|
return SecurityUtils.getLoginUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.orion.ops.framework.security.core.strategy;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.security.config.AuthorizeRequestsCustomizer;
|
||||||
|
import com.orion.ops.framework.security.config.SecurityConfig;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置文件 认证策略
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/7 13:04
|
||||||
|
*/
|
||||||
|
public class ConfigAuthorizeRequestsCustomizer extends AuthorizeRequestsCustomizer {
|
||||||
|
|
||||||
|
private final SecurityConfig securityConfig;
|
||||||
|
|
||||||
|
public ConfigAuthorizeRequestsCustomizer(SecurityConfig securityConfig) {
|
||||||
|
this.securityConfig = securityConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
|
||||||
|
// 配置文件 无需认证
|
||||||
|
registry.antMatchers(securityConfig.getPermitUrl().toArray(new String[0])).permitAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.orion.ops.framework.security.core.strategy;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.security.config.AuthorizeRequestsCustomizer;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 控制台 认证策略
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/7 13:04
|
||||||
|
*/
|
||||||
|
public class ConsoleAuthorizeRequestsCustomizer extends AuthorizeRequestsCustomizer {
|
||||||
|
|
||||||
|
private final String adminSeverContextPath;
|
||||||
|
|
||||||
|
public ConsoleAuthorizeRequestsCustomizer(String adminSeverContextPath) {
|
||||||
|
this.adminSeverContextPath = adminSeverContextPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
|
||||||
|
registry
|
||||||
|
// swagger 接口文档
|
||||||
|
.antMatchers("/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**").permitAll()
|
||||||
|
.antMatchers("/swagger-resources/**", "/webjars/**", "/*/api-docs").anonymous()
|
||||||
|
// druid 监控
|
||||||
|
.antMatchers("/druid/**").anonymous()
|
||||||
|
// actuator 安全配置 TODO TEST
|
||||||
|
.antMatchers("/actuator", "/actuator/**").anonymous()
|
||||||
|
// admin 安全配置 TODO TEST
|
||||||
|
.antMatchers(adminSeverContextPath, adminSeverContextPath + "/**").anonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package com.orion.ops.framework.security.core.strategy;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.security.config.AuthorizeRequestsCustomizer;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
|
||||||
|
import javax.annotation.security.PermitAll;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API @PermitAll 认证策略
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @see javax.annotation.security.PermitAll
|
||||||
|
* @since 2023/7/7 13:04
|
||||||
|
*/
|
||||||
|
public class PermitAllAnnotationAuthorizeRequestsCustomizer extends AuthorizeRequestsCustomizer {
|
||||||
|
|
||||||
|
private final ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
public PermitAllAnnotationAuthorizeRequestsCustomizer(ApplicationContext applicationContext) {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
|
||||||
|
// 获取匿名接口
|
||||||
|
Map<HttpMethod, Set<String>> permitAllUrls = getPermitAllUrlsFromAnnotations();
|
||||||
|
// @PermitAll 无需认证
|
||||||
|
registry.antMatchers(permitAllUrls.get(null).toArray(new String[0])).permitAll()
|
||||||
|
.antMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
|
||||||
|
.antMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()
|
||||||
|
.antMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll()
|
||||||
|
.antMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过注解获取所有匿名接口
|
||||||
|
*
|
||||||
|
* @return 匿名接口
|
||||||
|
*/
|
||||||
|
private Map<HttpMethod, Set<String>> getPermitAllUrlsFromAnnotations() {
|
||||||
|
Set<String> getList = new HashSet<>();
|
||||||
|
Set<String> postList = new HashSet<>();
|
||||||
|
Set<String> putList = new HashSet<>();
|
||||||
|
Set<String> deleteList = new HashSet<>();
|
||||||
|
Set<String> requestList = new HashSet<>();
|
||||||
|
// 获取 RequestMappingHandlerMapping
|
||||||
|
RequestMappingHandlerMapping requestMappingHandlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
|
||||||
|
// 获得接口对应的 HandlerMethod
|
||||||
|
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
|
||||||
|
// 获得有 @PermitAll 注解的接口
|
||||||
|
handlerMethodMap.forEach((mapping, method) -> {
|
||||||
|
// 非 @PermitAll 则跳过
|
||||||
|
if (!method.hasMethodAnnotation(PermitAll.class)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mapping.getPatternsCondition() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<String> urls = mapping.getPatternsCondition().getPatterns();
|
||||||
|
Set<RequestMethod> methods = mapping.getMethodsCondition().getMethods();
|
||||||
|
// 为空证明为 @RequestMapping
|
||||||
|
if (methods.isEmpty()) {
|
||||||
|
requestList.addAll(urls);
|
||||||
|
}
|
||||||
|
// 根据请求方法过滤
|
||||||
|
methods.forEach(requestMethod -> {
|
||||||
|
switch (requestMethod) {
|
||||||
|
case GET:
|
||||||
|
getList.addAll(urls);
|
||||||
|
break;
|
||||||
|
case POST:
|
||||||
|
postList.addAll(urls);
|
||||||
|
break;
|
||||||
|
case PUT:
|
||||||
|
putList.addAll(urls);
|
||||||
|
break;
|
||||||
|
case DELETE:
|
||||||
|
deleteList.addAll(urls);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// 设置返回
|
||||||
|
Map<HttpMethod, Set<String>> result = new HashMap<>();
|
||||||
|
result.put(HttpMethod.GET, getList);
|
||||||
|
result.put(HttpMethod.POST, postList);
|
||||||
|
result.put(HttpMethod.PUT, putList);
|
||||||
|
result.put(HttpMethod.DELETE, deleteList);
|
||||||
|
result.put(null, requestList);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.orion.ops.framework.security.core.strategy;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.security.config.AuthorizeRequestsCustomizer;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 静态资源 认证策略
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/7 13:04
|
||||||
|
*/
|
||||||
|
public class StaticResourceAuthorizeRequestsCustomizer extends AuthorizeRequestsCustomizer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
|
||||||
|
// 静态资源可匿名访问
|
||||||
|
registry.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.orion.ops.framework.security.core.strategy;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.security.config.AuthorizeRequestsCustomizer;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* websocket 认证策略
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/7 13:04
|
||||||
|
*/
|
||||||
|
public class WebsocketAuthorizeRequestsCustomizer extends AuthorizeRequestsCustomizer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
|
||||||
|
// websocket 允许匿名访问
|
||||||
|
registry.antMatchers("/keep-alive/**").permitAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package com.orion.ops.framework.security.core.utils;
|
||||||
|
|
||||||
|
import com.orion.lang.constant.StandardHttpHeader;
|
||||||
|
import com.orion.lang.utils.Strings;
|
||||||
|
import com.orion.ops.framework.common.constant.Const;
|
||||||
|
import com.orion.ops.framework.common.security.LoginUser;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全工具类
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/7 11:13
|
||||||
|
*/
|
||||||
|
public class SecurityUtils {
|
||||||
|
|
||||||
|
private SecurityUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 token
|
||||||
|
*
|
||||||
|
* @param request request
|
||||||
|
* @return token
|
||||||
|
*/
|
||||||
|
public static String obtainAuthorization(HttpServletRequest request) {
|
||||||
|
String authorization = request.getHeader(StandardHttpHeader.AUTHORIZATION);
|
||||||
|
// todo mock
|
||||||
|
authorization = "Bearer 1213";
|
||||||
|
if (Strings.isEmpty(authorization)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!authorization.contains(Const.BEARER)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return authorization.substring(7).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前认证信息
|
||||||
|
*
|
||||||
|
* @return 认证信息
|
||||||
|
*/
|
||||||
|
public static Authentication getAuthentication() {
|
||||||
|
SecurityContext context = SecurityContextHolder.getContext();
|
||||||
|
if (context == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return context.getAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户
|
||||||
|
*
|
||||||
|
* @return 当前用户
|
||||||
|
*/
|
||||||
|
public static LoginUser getLoginUser() {
|
||||||
|
Authentication authentication = getAuthentication();
|
||||||
|
if (authentication == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户id
|
||||||
|
*
|
||||||
|
* @return id
|
||||||
|
*/
|
||||||
|
public static Long getLoginUserId() {
|
||||||
|
LoginUser loginUser = getLoginUser();
|
||||||
|
return loginUser != null ? loginUser.getId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前用户
|
||||||
|
*
|
||||||
|
* @param loginUser 登录用户
|
||||||
|
* @param request 请求
|
||||||
|
*/
|
||||||
|
public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
|
||||||
|
// 创建 authentication
|
||||||
|
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList());
|
||||||
|
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
// 设置上下文
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"name": "orion.security",
|
||||||
|
"type": "com.orion.ops.framework.security.config.SecurityConfig",
|
||||||
|
"sourceType": "com.orion.ops.framework.security.config.SecurityConfig"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "orion.security.password-encoder-length",
|
||||||
|
"type": "java.lang.Integer",
|
||||||
|
"description": "加密复杂度.",
|
||||||
|
"defaultValue": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "orion.security.permit-url",
|
||||||
|
"type": "java.util.List",
|
||||||
|
"description": "匿名接口."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
com.orion.ops.framework.security.config.OrionSecurityAutoConfiguration
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.orion.ops.launch.service;
|
||||||
|
|
||||||
|
import com.orion.lang.utils.collect.Lists;
|
||||||
|
import com.orion.ops.framework.common.security.LoginUser;
|
||||||
|
import com.orion.ops.framework.security.core.service.SecurityFrameworkService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO 基建模块实现 现在默认实现
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/7/7 10:57
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class EmptySecurityImpl implements SecurityFrameworkService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(String permission) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasRole(String role) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginUser getUserByToken(String token) {
|
||||||
|
// TODO MOCK
|
||||||
|
LoginUser user = new LoginUser();
|
||||||
|
user.setId(123L);
|
||||||
|
user.setUsername("username");
|
||||||
|
user.setNickname("nickname");
|
||||||
|
user.setRoles(Lists.of("r1", "r2"));
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -66,7 +66,7 @@ spring:
|
|||||||
time-to-live: 1h
|
time-to-live: 1h
|
||||||
output:
|
output:
|
||||||
ansi:
|
ansi:
|
||||||
enabled: detect
|
enabled: DETECT
|
||||||
|
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
configuration:
|
configuration:
|
||||||
@@ -92,7 +92,7 @@ springdoc:
|
|||||||
knife4j:
|
knife4j:
|
||||||
enable: true
|
enable: true
|
||||||
setting:
|
setting:
|
||||||
language: zh_cn
|
language: ZH_CN
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
file:
|
file:
|
||||||
@@ -144,3 +144,7 @@ orion:
|
|||||||
nameAppendTraceId: true
|
nameAppendTraceId: true
|
||||||
storagePath: ${user.home}
|
storagePath: ${user.home}
|
||||||
basePath: /orion/storage/orion-ops-pro
|
basePath: /orion/storage/orion-ops-pro
|
||||||
|
security:
|
||||||
|
password-encoder-length: 4
|
||||||
|
# 匿名接口
|
||||||
|
permit-url:
|
||||||
|
|||||||
Reference in New Issue
Block a user