Skip to content

该项目是一个基于 spring、spring-boot、spring-security 的权限管理系统。核心功能是简化登陆、角色、菜单管理以及授权,同时能够动态配置系统API的访问权限,颗粒度到请求的方法。系统使用token作为访问凭据,实现了token的自动续期,内置了map和redis两种token存储的实现,开箱即用。

Notifications You must be signed in to change notification settings

dyw770/web-token-auth

Repository files navigation

EASY-AUTH-SYSTEM

项目简介

该项目是一个基于 springspring-bootspring-security 的权限管理系统。核心功能是简化登陆、角色、菜单管理以及授权,同时能够动态配置系统API的访问权限,颗粒度到请求的方法。系统使用token作为访问凭据,实现了token的自动续期,内置了mapredis两种token存储的实现,开箱即用。

模块说明

  1. auth-core 授权核心模块,实现token鉴权的核心逻辑,封装了token的生成、存储、验证、续期、销毁等核心功能。导入该模块后只需少量的配置即可实现token模式的登陆登出。模块内置mapredis两种token存储模式,可以通过配置切换,也可以实现自己的token存储。
  2. auth-db 数据库模块,实现权限数据存储,目前仅支持mysql。提供了一个spring-security的配置,将数据库中的权限配置加载到spring-security中,同时也对外提供了权限的编辑接口,将该模块加载后即可实权限的动态配置。
  3. auth-cache 缓存模块,实现权限缓存,目前仅支持redis。由于使用token访问系统时每次都需要去数据库中加载权限和角色信息数据库,如果想减轻数据库压力可以引入此模块,引入后配置缓存相关配置后auth-db模块会自动使用缓存。
  4. auth-sync 当权限变动时auth-db模块会发布 AuthChangedApplicationEvent.java 事件,在水平扩展情况下其他节点无法感知到权限数据发生了改变。如果需要多实例部署则可引入该模块,该模块使用redis的发布订阅来实现权限改变事件的监听,当权限数据发生改变时,会通知其他节点,其他节点收到消息后,会重新加载权限数据。
  5. auth-support 模块提供了一些系统中常用的功能,如使用 SystemEvent.java 注解来记录系统日志,以及使用 EnableSystemAccess.java 注解来开启访问日志记录。
  6. auth-ui 模块提供权限管理界面,基于 Fantastic-admin 实现,实现了常用的用户管理、角色管理、菜单管理、权限管理、日志查询、API授权管理功能
  7. auth-demo demo示例

快速开始

新建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访问则只需要引入该模块即可,可以通过@PreAuthorizespring 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实现即可切换缓存的存储实现。

管理UI

基于auth-db模块提供的权限管理APIauth-ui模块实现了一个UI管理界面。部分界面如图:

角色管理

用户管理

API资源管理

API授权

权限管理

菜单管理

菜单权限

访问日志

系统事件

其他功能

auth-support模块提供了两个注解EnableSystemAccessEnableSystemEvent

EnableSystemAccess注解用来记录系统的访问日志,默认由DefaultSystemAccessHandler.java记录到日志中,可以自定义实现来记录。在auth-db模块提供了JdbcSystemAccessHandler.javajdbc实现,可以直接注入该类。

EnableSystemEvent注解用来启用SystemEvent注解,该注解用来记录系统事件,默认由DefaultSystemEventHandler.java来记录,可以自定义实现来记录。在auth-db模块提供了JdbcSystemEventHandler.javajdbc实现,可以直接注入该类。SystemEvent注解提供了spel能力,可以通过spel获取参数或者调用方法,详细用法可以参考UserController.java中的login()方法。

About

该项目是一个基于 spring、spring-boot、spring-security 的权限管理系统。核心功能是简化登陆、角色、菜单管理以及授权,同时能够动态配置系统API的访问权限,颗粒度到请求的方法。系统使用token作为访问凭据,实现了token的自动续期,内置了map和redis两种token存储的实现,开箱即用。

Resources

Stars

Watchers

Forks

Packages

No packages published