该项目是一个基于 spring、spring-boot、spring-security 的权限管理系统。核心功能是简化登陆、角色、菜单管理以及授权,同时能够动态配置系统API的访问权限,颗粒度到请求的方法。系统使用token作为访问凭据,实现了token的自动续期,内置了map和redis两种token存储的实现,开箱即用。
auth-core授权核心模块,实现token鉴权的核心逻辑,封装了token的生成、存储、验证、续期、销毁等核心功能。导入该模块后只需少量的配置即可实现token模式的登陆登出。模块内置map和redis两种token存储模式,可以通过配置切换,也可以实现自己的token存储。auth-db数据库模块,实现权限数据存储,目前仅支持mysql。提供了一个spring-security的配置,将数据库中的权限配置加载到spring-security中,同时也对外提供了权限的编辑接口,将该模块加载后即可实权限的动态配置。auth-cache缓存模块,实现权限缓存,目前仅支持redis。由于使用token访问系统时每次都需要去数据库中加载权限和角色信息数据库,如果想减轻数据库压力可以引入此模块,引入后配置缓存相关配置后auth-db模块会自动使用缓存。auth-sync当权限变动时auth-db模块会发布 AuthChangedApplicationEvent.java 事件,在水平扩展情况下其他节点无法感知到权限数据发生了改变。如果需要多实例部署则可引入该模块,该模块使用redis的发布订阅来实现权限改变事件的监听,当权限数据发生改变时,会通知其他节点,其他节点收到消息后,会重新加载权限数据。auth-support模块提供了一些系统中常用的功能,如使用 SystemEvent.java 注解来记录系统日志,以及使用 EnableSystemAccess.java 注解来开启访问日志记录。auth-ui模块提供权限管理界面,基于 Fantastic-admin 实现,实现了常用的用户管理、角色管理、菜单管理、权限管理、日志查询、API授权管理功能auth-demodemo示例
新建springboot项目,引入auth-core模块,添加如下依赖:
<dependency>
<groupId>cn.dyw</groupId>
<artifactId>auth-core</artifactId>
<version>1.0.0</version>
</dependency>在启动类中使用@EnableAuthCore启用auth-core模块:
import cn.dyw.auth.security.configuration.EnableAuthCore;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableAuthCore
@SpringBootApplication
public class AuthDemoApplication {
public static void main(String[] args) {
SpringApplication.run(AuthDemoApplication.class, args);
}
}配置spring security需要忽略的url:
@Bean
public AuthorizeHttpRequestsCustomizer authorizeHttpRequestsCustomizer() throws Exception {
return (authorize) -> authorize
.requestMatchers("/user/login").permitAll()
.requestMatchers("/error").permitAll()
.requestMatchers("/doc/**").permitAll()
.requestMatchers("/favicon.ico").permitAll();
}在application.yml中配置token存储方式和token的请求头:
app:
auth:
token-repository: local
auth-header-name: Authorization # 默认值为 Authorization注入自己项目的UserDetailsService(auth-db模块提供了mysql的实现,如果引入该模块则无需配置自定义的UserDetailsService):
@Bean
@SuppressWarnings("deprecation")
@ConditionalOnMissingBean(UserDetailsService.class)
public UserDetailsService userDetailsService() {
// 仅测试示例
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}新建LoginController实现登陆和登出逻辑:
import cn.dyw.auth.message.Result;
import cn.dyw.auth.security.LoginLogoutHandler;
import cn.dyw.auth.security.TokenAuthenticationToken;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotBlank;
import org.springframework.security.core.Authentication;
import org.springframework.validation.annotation.Validated;
@RestController
@RequestMapping("/user")
public class LoginController {
private final LoginLogoutHandler loginLogoutHandler;
public LoginController(LoginLogoutHandler loginLogoutHandler) {
this.loginLogoutHandler = loginLogoutHandler;
}
/**
* 登录
*
* @param loginRq 账号和密码
* @return 结果
*/
@PostMapping("/login")
public Result<String> login(@RequestBody @Validated LoginRq loginRq, HttpServletRequest request) {
TokenAuthenticationToken login = loginLogoutHandler.login(loginRq.username(), loginRq.password(), request);
return Result.createSuccess("登录成功", login.getToken().token());
}
/**
* 登出
*
* @param authentication 授权信息
* @param request request
* @param response response
* @return 结果
*/
@GetMapping("/logout")
public Result<String> logout(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
loginLogoutHandler.logout(authentication, request, response);
return Result.createSuccess("登出成功");
}
/**
* @param username 用户名
* @param password 密码
*/
public record LoginRq(
@NotBlank
String username,
@NotBlank
String password
) {
}
}LoginLogoutHandler类封装了登陆和登出逻辑,调用login()方法会返回用户登陆后的token,将token返回给前台即可。如果登陆失败则会抛出异常,只需要注册自己的全局异常处理即可自定义错误处理结果,可以参考auth-demo模块中的GlobalDefaultExceptionHandler.java
完成以上配置后,访问/user/login接口即可进行登陆,登陆成功后访问其他接口需要携带登陆获取到的token,如果token无效则会抛出异常,可以通过全局异常处理捕获到该异常,可以参考可以参考auth-demo模块中的GlobalDefaultExceptionHandler.java。同时该模块还内置了一个UserManageSupportController.java接口,用来获取用户的登陆信息,以及强制下线功能。
默认token要求放在请求头中,可以通过app.auth.auth-header-name属性修改请求头名称,默认值为Authorization, 请求示例:Authorization: Bearer ${token}。可以通过自定义TokenResolve来实现自己的token获取逻辑。
auth-core模块只提供了基础授权逻辑,并无权限相关实现,但是该模块完全基于spring security,因此spring security中的注解依然生效。如果不需要auth-db模块提供的动态权限功能,只需要实现token访问则只需要引入该模块即可,可以通过@PreAuthorize等spring security注解控制权限。
如果需要权限动态控制则可以引入auth-db模块,该模块提供了用户的角色管理、权限管理、菜单管理、api访问权限管理。在上述项目中引入如下依赖并在启动类中增加@EnableJdbcAuth注解启用。
<dependency>
<groupId>cn.dyw</groupId>
<artifactId>auth-db</artifactId>
<version>1.0.0</version>
</dependency>配置mysql数据库连接,并且执行auth-db模块提供的数据库脚本db.sql,数据库内置了admin用户,密码为123456。
如果需要水平扩展部署还需要引入auth-sync模块,同时需要redis组件,该模块依赖redis来发布订阅权限改变事件来实现各节点之间的权限数据同步。引入该模块并配置redis连接信息即可。
<dependency>
<groupId>cn.dyw</groupId>
<artifactId>auth-sync</artifactId>
<version>1.0.0</version>
</dependency>权限信息在每次访问时都需要从db中加载角色信息和权限信息,如果访问比较频繁可以引入auth-cache模块来缓存角色信息和权限信息以减少数据库的压力,该模块只支持redis缓存,如果需要其他缓存只需要注入CacheManager实现即可切换缓存的存储实现。
基于auth-db模块提供的权限管理APIauth-ui模块实现了一个UI管理界面。部分界面如图:
auth-support模块提供了两个注解EnableSystemAccess、EnableSystemEvent。
EnableSystemAccess注解用来记录系统的访问日志,默认由DefaultSystemAccessHandler.java记录到日志中,可以自定义实现来记录。在auth-db模块提供了JdbcSystemAccessHandler.javajdbc实现,可以直接注入该类。
EnableSystemEvent注解用来启用SystemEvent注解,该注解用来记录系统事件,默认由DefaultSystemEventHandler.java来记录,可以自定义实现来记录。在auth-db模块提供了JdbcSystemEventHandler.javajdbc实现,可以直接注入该类。SystemEvent注解提供了spel能力,可以通过spel获取参数或者调用方法,详细用法可以参考UserController.java中的login()方法。








