包结构
这里记录一下学习eladmin项目的笔记,为了回顾,也为了共同学习。
项目结构
这里借鉴官网对项目结构的介绍:
eladmin-common
:为系统的公共模块,各种工具类,公共配置存在该模块。eladmin-system
:为系统核心模块也是项目入口模块, 也是最终需要打包部署的模块。eladmin-logging
:为系统的日志模块,其他模块如果需要记录日志需要引入该模块。eladmin-tools
:为第三方工具模块,包含:图床、邮件、云存储、本地存储、支付宝eladmin-generator
:为系统的代码生成模块,代码生成的模板在system模块中。
详细结构
- eladmin-common 公共模块
- annotation 为系统自定义注解
- aspect 自定义注解的切面
- base 提供了Entity、DTO基类和mapstruct的通用mapper
- config 自定义权限实现、redis配置、swagger配置、Rsa配置等
- exception 项目统一异常的处理
- utils 系统通用工具类
- eladmin-system 系统核心模块(系统启动入口)
- config 配置跨域与静态资源,与数据权限
- thread 线程池相关
- modules 系统相关模块(登录授权、系统监控、定时任务、运维管理等)
- eladmin-logging 系统日志模块
- eladmin-tools 系统第三方工具模块
- eladmin-generator 系统代码生成模块
这里先详细看看eladmin-system
的具体结构
eladmin-system
首先查看modules
包下面的security
包,这是由Spring Security
构成的负责系统认证和权限管理的模块。
先查看其中的配置类,SecurityConfig.java
,一般来说,都会有配置类继承类WebSecurityConfigurerAdapter
。
1.grantedAuthorityDefaults
首先使用@Bean
注解,注册了grantedAuthorityDefaults
。
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
// 去除 ROLE_ 前缀
return new GrantedAuthorityDefaults("");
}
2.passwordEncoder
然后注册了密码加密方式
@Bean
public PasswordEncoder passwordEncoder() {
// 密码加密方式
return new BCryptPasswordEncoder();
}
3.configure
重写configure
,目的是为了配置自身需要的安全方式。
这里作者首先,寻找使用了自定义的注解@AnonymousAccess
可以匿名访问的路由地址进行搜寻
// 搜寻匿名标记 url: @AnonymousAccess
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
当获取到这些请求类型之后,开始获取这些匿名请求的注解。具体的函数封装在了getAnonymousUrl
函数中。
//获取匿名标记
Map<String, Set<String>> anonymousUrls = getAnonymousUrl(handlerMethodMap);
//getAnonymousUrl函数
private Map<String, Set<String>> getAnonymousUrl(Map<RequestMappingInfo, HandlerMethod> handlerMethodMap) {
Map<String, Set<String>> anonymousUrls = new HashMap<>(6);
Set<String> get = new HashSet<>();
Set<String> post = new HashSet<>();
Set<String> put = new HashSet<>();
Set<String> patch = new HashSet<>();
Set<String> delete = new HashSet<>();
Set<String> all = new HashSet<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
if (null != anonymousAccess) {
List<RequestMethod> requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());
RequestMethodEnum request = RequestMethodEnum.find(requestMethods.size() == 0 ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name());
switch (Objects.requireNonNull(request)) {
case GET:
get.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case POST:
post.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case PUT:
put.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case PATCH:
patch.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case DELETE:
delete.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
default:
all.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
}
}
}
anonymousUrls.put(RequestMethodEnum.GET.getType(), get);
anonymousUrls.put(RequestMethodEnum.POST.getType(), post);
anonymousUrls.put(RequestMethodEnum.PUT.getType(), put);
anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch);
anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete);
anonymousUrls.put(RequestMethodEnum.ALL.getType(), all);
return anonymousUrls;
}
除此之外还定义securityConfigurerAdapter
方法,返回的对象为TokenConfigurer
。
private TokenConfigurer securityConfigurerAdapter() {
return new TokenConfigurer(tokenProvider, properties, onlineUserService, userCacheClean);
}
TokenConfigurer
为一个类,下面看看,这个类的代码。
@RequiredArgsConstructor
public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final TokenProvider tokenProvider;
private final SecurityProperties properties;
private final OnlineUserService onlineUserService;
private final UserCacheClean userCacheClean;
@Override
public void configure(HttpSecurity http) {
TokenFilter customFilter = new TokenFilter(tokenProvider, properties, onlineUserService, userCacheClean);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
这里涉及到以下知识:
@RequiredArgsConstructor
- 注解在类上,会生成构造方法(可能带参数也可能不带参数)。
注意:如果带参数,这个参数只能是以final修饰的未经初始化的字段或者是以
@NonNull
注解的未经初始化的字段。- 该注解还可以用
@RequiredArgsConstructor(staticName="methodName")
的形式生成一个指定名称的静态方法,返回一个调用相应的构造方法产生的对象。
// 使用注解
@RequiredArgsConstructor(staticName = "hangge")
public class Shape {
private int x;
@NonNull
private double y;
@NonNull
private String name;
}
// 不使用注解
public class Shape {
private int x;
private double y;
private String name;
public Shape(double y, String name){
this.y = y;
this.name = name;
}
public static Shape hangge(double y, String name){
return new Shape(y, name);
}
}
TokenFilter
类的代码则如下,这是TokenFilter的构造方法。
public TokenFilter(TokenProvider tokenProvider, SecurityProperties properties, OnlineUserService onlineUserService, UserCacheClean userCacheClean) {
this.properties = properties;
this.onlineUserService = onlineUserService;
this.tokenProvider = tokenProvider;
this.userCacheClean = userCacheClean;
}
addFilterBefore(A,B.class)
给A的序号比B小1,addFilterAfter(A,B.class)
给A的序号比B大1。