最新公众号管理平台后端
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4" />
|
||||
62
joju-framework/joju-spring-boot-starter-security/pom.xml
Normal file
62
joju-framework/joju-spring-boot-starter-security/pom.xml
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.jojubanking.boot</groupId>
|
||||
<artifactId>joju-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>joju-spring-boot-starter-security</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>用户的认证、权限的校验</description>
|
||||
<url>https://www.jojubanking.com</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.jojubanking.boot</groupId>
|
||||
<artifactId>joju-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring 核心 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.jojubanking.boot</groupId>
|
||||
<artifactId>joju-spring-boot-starter-web</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- spring boot 配置所需依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>com.jojubanking.boot</groupId>
|
||||
<artifactId>joju-module-system-api</artifactId> <!-- 需要使用它,进行 Token 的校验 -->
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.jojubanking.boot.framework.security.config;
|
||||
|
||||
import com.jojubanking.boot.framework.web.config.WebProperties;
|
||||
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;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 自定义的 URL 的安全配置
|
||||
* 目的:每个 Maven Module 可以自定义规则!
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public abstract class AuthorizeRequestsCustomizer
|
||||
implements Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry>, Ordered {
|
||||
|
||||
@Resource
|
||||
private WebProperties webProperties;
|
||||
|
||||
protected String buildAdminApi(String url) {
|
||||
return webProperties.getAdminApi().getPrefix() + url;
|
||||
}
|
||||
|
||||
protected String buildAppApi(String url) {
|
||||
return webProperties.getAppApi().getPrefix() + url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.jojubanking.boot.framework.security.config;
|
||||
|
||||
import com.jojubanking.boot.framework.security.core.aop.PreAuthenticatedAspect;
|
||||
import com.jojubanking.boot.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy;
|
||||
import com.jojubanking.boot.framework.security.core.filter.TokenAuthenticationFilter;
|
||||
import com.jojubanking.boot.framework.security.core.handler.AccessDeniedHandlerImpl;
|
||||
import com.jojubanking.boot.framework.security.core.handler.AuthenticationEntryPointImpl;
|
||||
import com.jojubanking.boot.framework.security.core.service.SecurityFrameworkService;
|
||||
import com.jojubanking.boot.framework.security.core.service.SecurityFrameworkServiceImpl;
|
||||
import com.jojubanking.boot.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import com.jojubanking.boot.module.system.api.oauth2.OAuth2TokenApi;
|
||||
import com.jojubanking.boot.module.system.api.permission.PermissionApi;
|
||||
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.access.AccessDeniedHandler;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* Spring Security 自动配置类,主要用于相关组件的配置
|
||||
*
|
||||
* 注意,不能和 {@link JojuWebSecurityConfigurerAdapter} 用一个,原因是会导致初始化报错。
|
||||
* 参见 https://stackoverflow.com/questions/53847050/spring-boot-delegatebuilder-cannot-be-null-on-autowiring-authenticationmanager 文档。
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
public class JojuSecurityAutoConfiguration {
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
/**
|
||||
* 处理用户未登录拦截的切面的 Bean
|
||||
*/
|
||||
@Bean
|
||||
public PreAuthenticatedAspect preAuthenticatedAspect() {
|
||||
return new PreAuthenticatedAspect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证失败处理类 Bean
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationEntryPoint authenticationEntryPoint() {
|
||||
return new AuthenticationEntryPointImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限不够处理器 Bean
|
||||
*/
|
||||
@Bean
|
||||
public AccessDeniedHandler accessDeniedHandler() {
|
||||
return new AccessDeniedHandlerImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Spring Security 加密器
|
||||
* 考虑到安全性,这里采用 BCryptPasswordEncoder 加密器
|
||||
*
|
||||
* @see <a href="http://stackabuse.com/password-encoding-with-spring-security/">Password Encoding with Spring Security</a>
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Token 认证过滤器 Bean
|
||||
*/
|
||||
@Bean
|
||||
public TokenAuthenticationFilter authenticationTokenFilter(GlobalExceptionHandler globalExceptionHandler,
|
||||
OAuth2TokenApi oauth2TokenApi) {
|
||||
return new TokenAuthenticationFilter(securityProperties, globalExceptionHandler, oauth2TokenApi);
|
||||
}
|
||||
|
||||
@Bean("ss") // 使用 Spring Security 的缩写,方便使用
|
||||
public SecurityFrameworkService securityFrameworkService(PermissionApi permissionApi) {
|
||||
return new SecurityFrameworkServiceImpl(permissionApi);
|
||||
}
|
||||
|
||||
/**
|
||||
* 声明调用 {@link SecurityContextHolder#setStrategyName(String)} 方法,
|
||||
* 设置使用 {@link TransmittableThreadLocalSecurityContextHolderStrategy} 作为 Security 的上下文策略
|
||||
*/
|
||||
@Bean
|
||||
public MethodInvokingFactoryBean securityContextHolderMethodInvokingFactoryBean() {
|
||||
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
|
||||
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
|
||||
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
|
||||
methodInvokingFactoryBean.setArguments(TransmittableThreadLocalSecurityContextHolderStrategy.class.getName());
|
||||
return methodInvokingFactoryBean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.jojubanking.boot.framework.security.config;
|
||||
|
||||
import com.jojubanking.boot.framework.security.core.filter.TokenAuthenticationFilter;
|
||||
import com.jojubanking.boot.framework.web.config.WebProperties;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
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.Resource;
|
||||
import javax.annotation.security.PermitAll;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 自定义的 Spring Security 配置适配器实现
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public class JojuWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Resource
|
||||
private WebProperties webProperties;
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
/**
|
||||
* 认证失败处理类 Bean
|
||||
*/
|
||||
@Resource
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
/**
|
||||
* 权限不够处理器 Bean
|
||||
*/
|
||||
@Resource
|
||||
private AccessDeniedHandler accessDeniedHandler;
|
||||
/**
|
||||
* Token 认证过滤器 Bean
|
||||
*/
|
||||
@Resource
|
||||
private TokenAuthenticationFilter authenticationTokenFilter;
|
||||
|
||||
/**
|
||||
* 自定义的权限映射 Bean 们
|
||||
*
|
||||
* @see #configure(HttpSecurity)
|
||||
*/
|
||||
@Resource
|
||||
private List<AuthorizeRequestsCustomizer> authorizeRequestsCustomizers;
|
||||
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 由于 Spring Security 创建 AuthenticationManager 对象时,没声明 @Bean 注解,导致无法被注入
|
||||
* 通过覆写父类的该方法,添加 @Bean 注解,解决该问题
|
||||
*/
|
||||
@Override
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AuthenticationManager.class)
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置 URL 的安全配置
|
||||
*
|
||||
* anyRequest | 匹配所有请求路径
|
||||
* access | SpringEl表达式结果为true时可以访问
|
||||
* anonymous | 匿名可以访问
|
||||
* denyAll | 用户不能访问
|
||||
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
|
||||
* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
|
||||
* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
|
||||
* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
|
||||
* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
|
||||
* hasRole | 如果有参数,参数表示角色,则其角色可以访问
|
||||
* permitAll | 用户可以任意访问
|
||||
* rememberMe | 允许通过remember-me登录的用户访问
|
||||
* authenticated | 用户登录后可访问
|
||||
*/
|
||||
@Override
|
||||
protected void configure(HttpSecurity httpSecurity) throws Exception {
|
||||
// 登出
|
||||
httpSecurity
|
||||
// 开启跨域
|
||||
.cors().and()
|
||||
// CSRF 禁用,因为不使用 Session
|
||||
.csrf().disable()
|
||||
// 基于 token 机制,所以不需要 Session
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
||||
.headers().frameOptions().disable().and()
|
||||
// 一堆自定义的 Spring Security 处理器
|
||||
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
|
||||
.accessDeniedHandler(accessDeniedHandler);
|
||||
// 登录、登录暂时不使用 Spring Security 的拓展点,主要考虑一方面拓展多用户、多种登录方式相对复杂,一方面用户的学习成本较高
|
||||
|
||||
// 获得 @PermitAll 带来的 URL 列表,免登录
|
||||
Multimap<HttpMethod, String> permitAllUrls = getPermitAllUrlsFromAnnotations();
|
||||
// 设置每个请求的权限
|
||||
httpSecurity
|
||||
// ①:全局共享规则
|
||||
.authorizeRequests()
|
||||
// 1.1 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
|
||||
// 1.2 设置 @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()
|
||||
// 1.3 基于 joju.security.permit-all-urls 无需认证
|
||||
.antMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll()
|
||||
// 1.4 设置 App API 无需认证
|
||||
.antMatchers(buildAppApi("/**")).permitAll()
|
||||
// ②:每个项目的自定义规则
|
||||
.and().authorizeRequests(registry -> // 下面,循环设置自定义规则
|
||||
authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(registry)))
|
||||
// ③:兜底规则,必须认证
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
;
|
||||
|
||||
// 添加 Token Filter
|
||||
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
private String buildAppApi(String url) {
|
||||
return webProperties.getAppApi().getPrefix() + url;
|
||||
}
|
||||
|
||||
private Multimap<HttpMethod, String> getPermitAllUrlsFromAnnotations() {
|
||||
Multimap<HttpMethod, String> result = HashMultimap.create();
|
||||
// 获得接口对应的 HandlerMethod 集合
|
||||
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
|
||||
applicationContext.getBean("requestMappingHandlerMapping");
|
||||
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
|
||||
// 获得有 @PermitAll 注解的接口
|
||||
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
|
||||
HandlerMethod handlerMethod = entry.getValue();
|
||||
if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) {
|
||||
continue;
|
||||
}
|
||||
if (entry.getKey().getPatternsCondition() == null) {
|
||||
continue;
|
||||
}
|
||||
Set<String> urls = entry.getKey().getPatternsCondition().getPatterns();
|
||||
// 根据请求方法,添加到 result 结果
|
||||
entry.getKey().getMethodsCondition().getMethods().forEach(requestMethod -> {
|
||||
switch (requestMethod) {
|
||||
case GET:
|
||||
result.putAll(HttpMethod.GET, urls);
|
||||
break;
|
||||
case POST:
|
||||
result.putAll(HttpMethod.POST, urls);
|
||||
break;
|
||||
case PUT:
|
||||
result.putAll(HttpMethod.PUT, urls);
|
||||
break;
|
||||
case DELETE:
|
||||
result.putAll(HttpMethod.DELETE, urls);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.jojubanking.boot.framework.security.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@ConfigurationProperties(prefix = "joju.security")
|
||||
@Validated
|
||||
@Data
|
||||
public class SecurityProperties {
|
||||
|
||||
/**
|
||||
* HTTP 请求时,访问令牌的请求 Header
|
||||
*/
|
||||
@NotEmpty(message = "Token Header 不能为空")
|
||||
private String tokenHeader = "Authorization";
|
||||
|
||||
/**
|
||||
* mock 模式的开关
|
||||
*/
|
||||
@NotNull(message = "mock 模式的开关不能为空")
|
||||
private Boolean mockEnable = false;
|
||||
/**
|
||||
* mock 模式的密钥
|
||||
* 一定要配置密钥,保证安全性
|
||||
*/
|
||||
@NotEmpty(message = "mock 模式的密钥不能为空") // 这里设置了一个默认值,因为实际上只有 mockEnable 为 true 时才需要配置。
|
||||
private String mockSecret = "test";
|
||||
|
||||
/**
|
||||
* 免登录的 URL 列表
|
||||
*/
|
||||
private List<String> permitAllUrls = Collections.emptyList();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.jojubanking.boot.framework.security.core;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.jojubanking.boot.framework.common.enums.UserTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 登录用户信息
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class LoginUser {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 用户类型
|
||||
*
|
||||
* 关联 {@link UserTypeEnum}
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private Long tenantId;
|
||||
/**
|
||||
* 授权范围
|
||||
*/
|
||||
private List<String> scopes;
|
||||
|
||||
// ========== 上下文 ==========
|
||||
/**
|
||||
* 上下文字段,不进行持久化
|
||||
*
|
||||
* 1. 用于基于 LoginUser 维度的临时缓存
|
||||
*/
|
||||
@JsonIgnore
|
||||
private Map<String, Object> context;
|
||||
|
||||
public void setContext(String key, Object value) {
|
||||
if (context == null) {
|
||||
context = new HashMap<>();
|
||||
}
|
||||
context.put(key, value);
|
||||
}
|
||||
|
||||
public <T> T getContext(String key, Class<T> type) {
|
||||
return MapUtil.get(context, key, type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.jojubanking.boot.framework.security.core.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 声明用户需要登录
|
||||
*
|
||||
* 为什么不使用 {@link org.springframework.security.access.prepost.PreAuthorize} 注解,原因是不通过时,抛出的是认证不通过,而不是未登录
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface PreAuthenticated {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.jojubanking.boot.framework.security.core.aop;
|
||||
|
||||
import com.jojubanking.boot.framework.security.core.annotations.PreAuthenticated;
|
||||
import com.jojubanking.boot.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
|
||||
import static com.jojubanking.boot.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED;
|
||||
import static com.jojubanking.boot.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
|
||||
@Aspect
|
||||
@Slf4j
|
||||
public class PreAuthenticatedAspect {
|
||||
|
||||
@Around("@annotation(preAuthenticated)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, PreAuthenticated preAuthenticated) throws Throwable {
|
||||
if (SecurityFrameworkUtils.getLoginUser() == null) {
|
||||
throw exception(UNAUTHORIZED);
|
||||
}
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.jojubanking.boot.framework.security.core.context;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* 基于 TransmittableThreadLocal 实现的 Security Context 持有者策略
|
||||
* 目的是,避免 @Async 等异步执行时,原生 ThreadLocal 的丢失问题
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public class TransmittableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
|
||||
|
||||
/**
|
||||
* 使用 TransmittableThreadLocal 作为上下文
|
||||
*/
|
||||
private static final ThreadLocal<SecurityContext> contextHolder = new TransmittableThreadLocal<>();
|
||||
|
||||
@Override
|
||||
public void clearContext() {
|
||||
contextHolder.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityContext getContext() {
|
||||
SecurityContext ctx = contextHolder.get();
|
||||
if (ctx == null) {
|
||||
ctx = createEmptyContext();
|
||||
contextHolder.set(ctx);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(SecurityContext context) {
|
||||
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
|
||||
contextHolder.set(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityContext createEmptyContext() {
|
||||
return new SecurityContextImpl();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.jojubanking.boot.framework.security.core.filter;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.jojubanking.boot.framework.common.exception.ServiceException;
|
||||
import com.jojubanking.boot.framework.common.pojo.CommonResult;
|
||||
import com.jojubanking.boot.framework.common.util.servlet.ServletUtils;
|
||||
import com.jojubanking.boot.framework.security.config.SecurityProperties;
|
||||
import com.jojubanking.boot.framework.security.core.LoginUser;
|
||||
import com.jojubanking.boot.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.jojubanking.boot.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import com.jojubanking.boot.framework.web.core.util.WebFrameworkUtils;
|
||||
import com.jojubanking.boot.module.system.api.oauth2.OAuth2TokenApi;
|
||||
import com.jojubanking.boot.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
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 过滤器,验证 token 的有效性
|
||||
* 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final SecurityProperties securityProperties;
|
||||
|
||||
private final GlobalExceptionHandler globalExceptionHandler;
|
||||
|
||||
private final OAuth2TokenApi oauth2TokenApi;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NullableProblems")
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
|
||||
if (StrUtil.isNotEmpty(token)) {
|
||||
Integer userType = WebFrameworkUtils.getLoginUserType(request);
|
||||
try {
|
||||
// 1.1 基于 token 构建登录用户
|
||||
LoginUser loginUser = buildLoginUserByToken(token, userType);
|
||||
// 1.2 模拟 Login 功能,方便日常开发调试
|
||||
if (loginUser == null) {
|
||||
loginUser = mockLoginUser(request, token, userType);
|
||||
}
|
||||
|
||||
// 2. 设置当前用户
|
||||
if (loginUser != null) {
|
||||
SecurityFrameworkUtils.setLoginUser(loginUser, request);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex);
|
||||
ServletUtils.writeJSON(response, result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 继续过滤链
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private LoginUser buildLoginUserByToken(String token, Integer userType) {
|
||||
try {
|
||||
OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token);
|
||||
if (accessToken == null) {
|
||||
return null;
|
||||
}
|
||||
// 用户类型不匹配,无权限
|
||||
if (ObjectUtil.notEqual(accessToken.getUserType(), userType)) {
|
||||
throw new AccessDeniedException("错误的用户类型");
|
||||
}
|
||||
// 构建登录用户
|
||||
LoginUser loginUser = new LoginUser();
|
||||
loginUser.setUserType(accessToken.getUserType());
|
||||
loginUser.setId(accessToken.getUserId());
|
||||
loginUser.setTenantId(accessToken.getTenantId());
|
||||
loginUser.setScopes(accessToken.getScopes());
|
||||
return loginUser;
|
||||
// return new LoginUser().setUserType(accessToken.getUserType()).setId(accessToken.getUserId())
|
||||
// .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes());
|
||||
} catch (ServiceException serviceException) {
|
||||
// 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟登录用户,方便日常开发调试
|
||||
*
|
||||
* 注意,在线上环境下,一定要关闭该功能!!!
|
||||
*
|
||||
* @param request 请求
|
||||
* @param token 模拟的 token,格式为 {@link SecurityProperties#getMockSecret()} + 用户编号
|
||||
* @param userType 用户类型
|
||||
* @return 模拟的 LoginUser
|
||||
*/
|
||||
private LoginUser mockLoginUser(HttpServletRequest request, String token, Integer userType) {
|
||||
if (!securityProperties.getMockEnable()) {
|
||||
return null;
|
||||
}
|
||||
// 必须以 mockSecret 开头
|
||||
if (!token.startsWith(securityProperties.getMockSecret())) {
|
||||
return null;
|
||||
}
|
||||
// 构建模拟用户
|
||||
Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length()));
|
||||
|
||||
LoginUser loginUser = new LoginUser();
|
||||
loginUser.setUserType(userType);
|
||||
loginUser.setId(userId);
|
||||
loginUser.setTenantId(WebFrameworkUtils.getTenantId(request));
|
||||
return loginUser;
|
||||
|
||||
// return new LoginUser().setId(userId).setUserType(userType)
|
||||
// .setTenantId(WebFrameworkUtils.getTenantId(request));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.jojubanking.boot.framework.security.core.handler;
|
||||
|
||||
import com.jojubanking.boot.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import com.jojubanking.boot.framework.common.pojo.CommonResult;
|
||||
import com.jojubanking.boot.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.jojubanking.boot.framework.common.util.servlet.ServletUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.jojubanking.boot.framework.common.exception.enums.GlobalErrorCodeConstants.FORBIDDEN;
|
||||
import static com.jojubanking.boot.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED;
|
||||
|
||||
/**
|
||||
* 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。
|
||||
*
|
||||
* 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@Slf4j
|
||||
@SuppressWarnings("JavadocReference")
|
||||
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
|
||||
throws IOException, ServletException {
|
||||
// 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
|
||||
log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
|
||||
SecurityFrameworkUtils.getLoginUserId(), e);
|
||||
// 返回 403
|
||||
ServletUtils.writeJSON(response, CommonResult.error(FORBIDDEN));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.jojubanking.boot.framework.security.core.handler;
|
||||
|
||||
import com.jojubanking.boot.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import com.jojubanking.boot.framework.common.pojo.CommonResult;
|
||||
import com.jojubanking.boot.framework.common.util.servlet.ServletUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import static com.jojubanking.boot.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED;
|
||||
|
||||
/**
|
||||
* 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
|
||||
*
|
||||
* 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Slf4j
|
||||
@SuppressWarnings("JavadocReference") // 忽略文档引用报错
|
||||
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
|
||||
log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
|
||||
// 返回 401
|
||||
ServletUtils.writeJSON(response, CommonResult.error(UNAUTHORIZED));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.jojubanking.boot.framework.security.core.service;
|
||||
|
||||
/**
|
||||
* Security 框架 Service 接口,定义权限相关的校验操作
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public interface SecurityFrameworkService {
|
||||
|
||||
/**
|
||||
* 判断是否有权限
|
||||
*
|
||||
* @param permission 权限
|
||||
* @return 是否
|
||||
*/
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
/**
|
||||
* 判断是否有权限,任一一个即可
|
||||
*
|
||||
* @param permissions 权限
|
||||
* @return 是否
|
||||
*/
|
||||
boolean hasAnyPermissions(String... permissions);
|
||||
|
||||
/**
|
||||
* 判断是否有角色
|
||||
*
|
||||
* 注意,角色使用的是 SysRoleDO 的 code 标识
|
||||
*
|
||||
* @param role 角色
|
||||
* @return 是否
|
||||
*/
|
||||
boolean hasRole(String role);
|
||||
|
||||
/**
|
||||
* 判断是否有角色,任一一个即可
|
||||
*
|
||||
* @param roles 角色数组
|
||||
* @return 是否
|
||||
*/
|
||||
boolean hasAnyRoles(String... roles);
|
||||
|
||||
/**
|
||||
* 判断是否有授权
|
||||
*
|
||||
* @param scope 授权
|
||||
* @return 是否
|
||||
*/
|
||||
boolean hasScope(String scope);
|
||||
|
||||
/**
|
||||
* 判断是否有授权范围,任一一个即可
|
||||
*
|
||||
* @param scope 授权范围数组
|
||||
* @return 是否
|
||||
*/
|
||||
boolean hasAnyScopes(String... scope);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.jojubanking.boot.framework.security.core.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.jojubanking.boot.framework.security.core.LoginUser;
|
||||
import com.jojubanking.boot.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.jojubanking.boot.module.system.api.permission.PermissionApi;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.jojubanking.boot.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
/**
|
||||
* 默认的 {@link SecurityFrameworkService} 实现类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
|
||||
|
||||
private final PermissionApi permissionApi;
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return hasAnyPermissions(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermissions(String... permissions) {
|
||||
return permissionApi.hasAnyPermissions(getLoginUserId(), permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(String role) {
|
||||
return hasAnyRoles(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRoles(String... roles) {
|
||||
return permissionApi.hasAnyRoles(getLoginUserId(), roles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasScope(String scope) {
|
||||
return hasAnyScopes(scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyScopes(String... scope) {
|
||||
LoginUser user = SecurityFrameworkUtils.getLoginUser();
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
return CollUtil.containsAny(user.getScopes(), Arrays.asList(scope));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.jojubanking.boot.framework.security.core.util;
|
||||
|
||||
import com.jojubanking.boot.framework.security.core.LoginUser;
|
||||
import com.jojubanking.boot.framework.web.core.util.WebFrameworkUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
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 org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 安全服务工具类
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
public class SecurityFrameworkUtils {
|
||||
|
||||
public static final String AUTHORIZATION_BEARER = "Bearer";
|
||||
|
||||
private SecurityFrameworkUtils() {}
|
||||
|
||||
/**
|
||||
* 从请求中,获得认证 Token
|
||||
*
|
||||
* @param request 请求
|
||||
* @param header 认证 Token 对应的 Header 名字
|
||||
* @return 认证 Token
|
||||
*/
|
||||
public static String obtainAuthorization(HttpServletRequest request, String header) {
|
||||
String authorization = request.getHeader(header);
|
||||
if (!StringUtils.hasText(authorization)) {
|
||||
return null;
|
||||
}
|
||||
int index = authorization.indexOf(AUTHORIZATION_BEARER + " ");
|
||||
if (index == -1) { // 未找到
|
||||
return null;
|
||||
}
|
||||
return authorization.substring(index + 7).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前认证信息
|
||||
*
|
||||
* @return 认证信息
|
||||
*/
|
||||
public static Authentication getAuthentication() {
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
return context.getAuthentication();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户
|
||||
*
|
||||
* @return 当前用户
|
||||
*/
|
||||
@Nullable
|
||||
public static LoginUser getLoginUser() {
|
||||
Authentication authentication = getAuthentication();
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前用户的编号,从上下文中
|
||||
*
|
||||
* @return 用户编号
|
||||
*/
|
||||
@Nullable
|
||||
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,并设置到上下文
|
||||
Authentication authentication = buildAuthentication(loginUser, request);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
// 额外设置到 request 中,用于 ApiAccessLogFilter 可以获取到用户编号;
|
||||
// 原因是,Spring Security 的 Filter 在 ApiAccessLogFilter 后面,在它记录访问日志时,线上上下文已经没有用户编号等信息
|
||||
WebFrameworkUtils.setLoginUserId(request, loginUser.getId());
|
||||
WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType());
|
||||
}
|
||||
|
||||
private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
|
||||
// 创建 UsernamePasswordAuthenticationToken 对象
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
|
||||
loginUser, null, Collections.emptyList());
|
||||
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
return authenticationToken;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 基于 Spring Security 框架
|
||||
* 实现安全认证功能
|
||||
*
|
||||
* @author TW
|
||||
*/
|
||||
package com.jojubanking.boot.framework.security;
|
||||
@@ -0,0 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.jojubanking.boot.framework.security.config.JojuSecurityAutoConfiguration,\
|
||||
com.jojubanking.boot.framework.security.config.JojuWebSecurityConfigurerAdapter
|
||||
@@ -0,0 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.jojubanking.boot.framework.security.config.JojuSecurityAutoConfiguration,\
|
||||
com.jojubanking.boot.framework.security.config.JojuWebSecurityConfigurerAdapter
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
#Generated by Maven
|
||||
#Wed Jan 14 17:48:21 CST 2026
|
||||
version=2.0.0-beta
|
||||
groupId=com.jojubanking.boot
|
||||
artifactId=joju-spring-boot-starter-security
|
||||
@@ -0,0 +1,15 @@
|
||||
com\jojubanking\boot\framework\security\core\aop\PreAuthenticatedAspect.class
|
||||
com\jojubanking\boot\framework\security\config\SecurityProperties.class
|
||||
com\jojubanking\boot\framework\security\core\context\TransmittableThreadLocalSecurityContextHolderStrategy.class
|
||||
com\jojubanking\boot\framework\security\core\service\SecurityFrameworkService.class
|
||||
com\jojubanking\boot\framework\security\core\filter\TokenAuthenticationFilter.class
|
||||
com\jojubanking\boot\framework\security\config\AuthorizeRequestsCustomizer.class
|
||||
com\jojubanking\boot\framework\security\core\util\SecurityFrameworkUtils.class
|
||||
com\jojubanking\boot\framework\security\core\LoginUser.class
|
||||
com\jojubanking\boot\framework\security\core\handler\AuthenticationEntryPointImpl.class
|
||||
com\jojubanking\boot\framework\security\config\JojuSecurityAutoConfiguration.class
|
||||
com\jojubanking\boot\framework\security\core\service\SecurityFrameworkServiceImpl.class
|
||||
com\jojubanking\boot\framework\security\core\handler\AccessDeniedHandlerImpl.class
|
||||
com\jojubanking\boot\framework\security\config\JojuWebSecurityConfigurerAdapter.class
|
||||
com\jojubanking\boot\framework\security\config\JojuWebSecurityConfigurerAdapter$1.class
|
||||
com\jojubanking\boot\framework\security\core\annotations\PreAuthenticated.class
|
||||
@@ -0,0 +1,15 @@
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\core\context\TransmittableThreadLocalSecurityContextHolderStrategy.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\core\aop\PreAuthenticatedAspect.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\core\handler\AccessDeniedHandlerImpl.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\core\service\SecurityFrameworkService.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\config\AuthorizeRequestsCustomizer.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\core\service\SecurityFrameworkServiceImpl.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\config\SecurityProperties.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\core\util\SecurityFrameworkUtils.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\core\filter\TokenAuthenticationFilter.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\config\JojuSecurityAutoConfiguration.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\core\handler\AuthenticationEntryPointImpl.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\core\LoginUser.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\config\JojuWebSecurityConfigurerAdapter.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\core\annotations\PreAuthenticated.java
|
||||
D:\workspace\nxwj\掌医管理平台\jojuboot\joju-framework\joju-spring-boot-starter-security\src\main\java\com\jojubanking\boot\framework\security\package-info.java
|
||||
@@ -0,0 +1,2 @@
|
||||
* 芋道 Spring Security 入门:<http://www.iocoder.cn/Spring-Boot/Spring-Security/?joju>
|
||||
* Spring Security 基本概念:<http://www.iocoder.cn/Fight/Spring-Security-4-1-0-Basic-concept-description/?joju>
|
||||
Reference in New Issue
Block a user