当前位置: 首页 > article >正文

项目集成Spring Security认证部分

一、需求分析

  在本项目中,使用了Spring Security框架来进行认证和授权管理。由于是前后端分离的项目,所有认证的请求需要通过Token来验证身份,系统中包括了用户登录、角色授权以及资源访问控制等功能。

系统中的资源控制:

  • 白名单url:指的是任何用户都可以访问的资源(例如登录、注册等公共接口),这些接口不需要身份验证。
  • 需要控制的资源:除了白名单中的接口,其他接口都需要进行权限验证。这些资源通常对应系统的菜单和按钮,用户根据所分配的角色和权限,决定是否能访问对应的接口。
  • 授权管理:通过角色和资源的绑定,确保只有授权的用户可以访问指定的接口,未授权的用户则返回403权限拒绝。

二、代码解读

SecurityConfig 配置

SecurityConfig类主要负责Spring Security的配置,其中包括了认证授权的管理。重点代码如下:

@Configuration
public class SecurityConfig {

    @Autowired
    private JwtTokenAuthorizationManager jwtTokenAuthorizationManager;

    @Autowired
    private SecurityConfigProperties securityConfigProperties;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        List<String> ignoreUrl = securityConfigProperties.getIgnoreUrl();

        // 前后端分离,放行登录接口等白名单
        http.authorizeHttpRequests().antMatchers(ignoreUrl.toArray(new String[ignoreUrl.size()])).permitAll()
                .anyRequest().access(jwtTokenAuthorizationManager);

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 关闭session
        http.headers().cacheControl().disable(); // 关闭缓存
        http.csrf().disable(); // 禁用csrf

        return http.build();
    }
}
  • 白名单配置antMatchers方法用于配置哪些接口是公开的(如登录接口)。这些接口不需要Token验证。
  • 认证管理anyRequest().access(jwtTokenAuthorizationManager)表示其他所有接口都需要通过jwtTokenAuthorizationManager来进行Token认证。
  • 关闭Session:由于是无状态的认证方式,关闭了HTTP Session和缓存,确保每次请求都是独立的。
UserDetailsServiceImpl 实现

UserDetailsServiceImpl类负责加载用户信息,以供Spring Security进行认证。

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.selectByUsername(username);
        if (ObjectUtil.isEmpty(user) || user.getDataState().equals("1")) {
            throw new RuntimeException("用户登录失败");
        }
        return BeanUtil.toBean(user, UserAuth.class);
    }
}
  • 通过loadUserByUsername方法,查询数据库中的用户信息,并将其封装为UserAuth对象返回给Spring Security,供认证过程使用。
LoginServiceImpl 认证过程

LoginServiceImpl类处理了用户的登录请求,并生成JWT Token。

@Override
public UserVo login(LoginDto loginDto) {
    UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());
    Authentication authenticate = authenticationManager.authenticate(upat);
    if (!authenticate.isAuthenticated()) {
        throw new BaseException(BasicEnum.LOGIN_FAIL);
    }

    UserAuth userAuth = (UserAuth) authenticate.getPrincipal();
    UserVo userVo = BeanUtil.toBean(userAuth, UserVo.class);
    userVo.setPassword("");

    // 获取用户角色和资源,生成JWT
    List<Resource> resourceVoList = resourceMapper.findResourceVoListByUserId(userVo.getId());
    Set<String> resourceRequestPaths = resourceVoList.stream()
            .map(Resource::getRequestPath)
            .collect(Collectors.toSet());
    List<String> publicAccessUrls = securityConfigProperties.getPublicAccessUrls();
    resourceRequestPaths.addAll(publicAccessUrls);

    Map<String, Object> clamis = new HashMap<>();
    clamis.put("currentUser", JSONUtil.toJsonStr(userVo));
    String jwtToken = JwtUtil.createJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), jwtTokenManagerProperties.getTtl(), clamis);
    userVo.setUserToken(jwtToken);

    // 存储访问权限到Redis
    long ttl = Long.valueOf(jwtTokenManagerProperties.getTtl() / 1000);
    String accessUrlsCacheKey = CacheConstants.ACCESS_URLS_CACHE + userVo.getId();
    redisTemplate.opsForValue().set(accessUrlsCacheKey, JSONUtil.toJsonStr(resourceRequestPaths), ttl, TimeUnit.SECONDS);

    return userVo;
}

1. 使用认证管理器进行认证

  • 用户通过提供的用户名和密码进行登录,系统首先创建UsernamePasswordAuthenticationToken对象,这个对象封装了用户的用户名和密码。
  • 然后,authenticationManager.authenticate(upat)方法会使用Spring Security的认证管理器来验证用户身份。如果认证失败,抛出自定义的BaseException,提示登录失败。

2. 获取用户数据

  • 当认证通过后,authenticate.getPrincipal()会返回包含用户信息的UserAuth对象。此对象包含了用户的基本信息(如用户名、密码等)。
  • 使用BeanUtil.toBean()方法将UserAuth对象转换为UserVo对象,便于后续的处理。

3. 获取用户资源权限列表

  • resourceMapper.findResourceVoListByUserId(userVo.getId())方法会查询数据库,获取当前用户拥有的资源权限,即用户可以访问的接口路径。
  • 这些路径将被存储在resourceRequestPaths集合中。

4. 合并公共访问的URL与用户资源路径

  • 系统还从配置文件中读取publicAccessUrls,即那些不需要认证的公共接口路径。
  • 将这些公共路径和用户可访问的资源路径合并,确保最终的访问列表包含了所有允许访问的接口。

5. 生成JWT Token

  • 使用JwtUtil.createJWT()方法创建一个JWT Token。这个Token包含了当前用户的信息(通过claims.put("currentUser", JSONUtil.toJsonStr(userVo))添加),并且设置了有效期(由jwtTokenManagerProperties.getTtl()控制)。
  • 生成的JWT Token被设置到userVo对象的userToken属性中,准备返回给前端。

6. 存储用户资源路径到Redis

  • 生成JWT Token后,系统会将用户可以访问的URL列表存储到Redis缓存中。这样做是为了提高性能,使得后续请求可以快速验证用户的权限。
  • 存储时设置了TTL(过期时间),使得权限数据与JWT Token的有效期一致。

7. 返回用户信息和JWT Token

  • 最终,系统返回一个UserVo对象,包含用户的基本信息和生成的JWT Token。前端可以使用这个Token进行后续的认证请求。
sql部分的编写:

​​​​​
<select id="selectByUserId" resultType="com.zzyl.entity.Resource">
  select sr.request_path requestPath
  from sys_user_role sur, sys_role_resource srr, sys_resource sr
  where sur.role_id = srr.role_id
    and srr.resource_no = sr.resource_no
    and sr.resource_type = 'r'
    and sr.data_state = '0'
    and sur.user_id = #{userId}
</select>
  1. 表结构与关联

    • sys_user_role:该表将用户与角色关联起来。字段包括user_idrole_id,用于表示用户与角色的关系。
    • sys_role_resource:该表将角色与资源关联起来。字段包括role_idresource_no,用于表示角色与资源的关系。
    • sys_resource:该表包含了所有系统资源的详细信息。字段包括resource_norequest_path(资源路径)、resource_type等,用于存储系统中所有的资源信息。
  2. SQL查询过程

    • 通过sys_user_role表和sys_role_resource表的连接,获取当前用户(由sur.user_id表示)所拥有的所有角色。
    • 然后,通过sys_role_resource表和sys_resource表的连接,获取这些角色对应的资源路径。
    • 查询条件sr.resource_type = 'r'限定了只选择类型为“按钮”的记录,sr.data_state = '0'表示只选择状态为“有效”的资源。
  3. 查询结果

    • 查询的结果是该用户可以访问的所有资源的request_path(资源路径)。这些路径将用于后续的权限控制。

使用场景

  • 该SQL查询用于获取当前用户的权限数据。通过用户的user_id,系统可以查询到该用户所有角色对应的资源路径,并且只返回这些有效的资源路径(sr.data_state = '0'表示资源有效)。这些资源路径将会存储在urlList中,用于生成JWT Token时添加到Token的有效载荷中。

三、总结

  在Spring Security认证与授权集成中,主要分为用户登录认证、JWT Token生成与校验以及权限管理等部分。


http://www.kler.cn/a/529024.html

相关文章:

  • 使用Pygame制作“俄罗斯方块”游戏
  • Docker 部署 GLPI(IT 资产管理软件系统)
  • 边缘检测算法(candy)
  • 电气相关知识
  • 【C++】类与对象(下)
  • 19.Word:小马-校园科技文化节❗【36】
  • 深入解析 C++ 字符串处理:提取和分割的多种方法
  • 14JavaWeb——SpringBoot原理
  • JWT入门
  • JAVA实战开源项目:甘肃非物质文化网站(Vue+SpringBoot) 附源码
  • leetcode——验证二叉搜索树(java)
  • 17.2 图形绘制6
  • Spring的应用场景和优势
  • Signature
  • 数据结构(栈结构之顺序栈操作实现一)
  • C++ 字母大小写转换两种方法统计数字字符的个数
  • 【股票数据API接口47】如何获取股票指历史分时KDJ数据之Python、Java等多种主流语言实例代码演示通过股票数据接口获取数据
  • 远程连接-简化登录
  • 进程控制-前篇
  • OpenCV:SURF、OBR特征检测
  • IS-IS 数据包类型 | 实验
  • TCL C++开发面试题及参考答案
  • Docker容器数据恢复
  • 【实战篇章】深入探讨:服务器如何响应前端请求及后端如何查看前端提交的数据
  • Autogen_core源码:_cache_store.py
  • C# 类与对象详解