项目集成Spring Security认证部分

news/2025/2/3 11:11:15 标签: spring, java, 后端

一、需求分析

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

系统中的资源控制:

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

二、代码解读

SecurityConfig 配置

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

java">@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进行认证。

java">@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。

java">@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.niftyadmin.cn/n/5840733.html

相关文章

React中useState()钩子和函数式组件底层渲染流程详解

useState()钩子底层渲染流程 React中useState的底层渲染机理。首先&#xff0c;我知道useState是React Hooks的一部分&#xff0c;用于在函数组件中添加状态。但底层是如何工作的呢&#xff1f;可能涉及到React的调度器、Fiber架构以及闭包等概念。 首先&#xff0c;React使用F…

网络工程师 (14)保护期限

前言 保护期限是指权利人在法律上享有的某种权利或利益的有效期限&#xff0c;这一期限根据不同的法律体系和权利类型而有所不同。 一、著作权保护期限 著作权是指作者对其创作的文学、艺术和科学作品享有的专有权利。根据《中华人民共和国著作权法》的规定&#xff1a; 公民的…

redis原理之数据结构

dict dict&#xff0c;哈希表&#xff0c;redis 所有的 key-value 都存储在里面。如果曾经学过哈希表这种数据结构&#xff0c;那么很容易能写出一个来&#xff0c;但 redis dict 考虑了更多的功能。 // 哈希表&#xff08;字典&#xff09;数据结构&#xff0c;redis 的所有键…

Spring源码的模块结构指南

Spring框架是一个广泛使用的开源框架&#xff0c;主要用于企业级应用开发&#xff0c;特别是在Java生态系统中。Spring的源码结构比较复杂&#xff0c;包含多个模块&#xff0c;每个模块有特定的功能。下面是Spring源码的主要模块结构详细输出&#xff1a; 1. Spring Core&…

kamailio-ACC模块介绍【kamailio6.0. X】

Acc 模块 作者 Jiri Kuthan iptel.org jiriiptel.org Bogdan-Andrei Iancu Voice Sistem SRL bogdanvoice-system.ro Ramona-Elena Modroiu rosdev.ro ramonarosdev.ro 编辑 Bogdan-Andrei Iancu Voice Sistem SRL bogdanvoice-system.ro Sven Knoblich 1&1 Internet …

Web3.js详解

Web1&Web2&Web3 以下是Web1、Web2和Web3的详细介绍&#xff0c;以及一个对比表格&#xff1a; Web1 定义&#xff1a;Web1指的是有着固定内容的非许可的开源网络。特点&#xff1a;在Web1时代&#xff0c;网站内容主要由网站管理员或创建者提供&#xff0c;用户只能…

Python NumPy(12):NumPy 字节交换、NumPy 副本和视图、NumPy 矩阵库(Matrix)

1 NumPy 字节交换 在几乎所有的机器上&#xff0c;多字节对象都被存储为连续的字节序列。字节顺序&#xff0c;是跨越多字节的程序对象的存储规则。 大端模式&#xff1a;指数据的高字节保存在内存的低地址中&#xff0c;而数据的低字节保存在内存的高地址中&#xff0c;这样的…

音视频入门基础:RTP专题(8)——使用Wireshark分析RTP

一、引言 通过Wireshark可以抓取RTP数据包&#xff0c;该软件可以从Wireshark Go Deep 下载。 二、通过Wireshark抓取RTP数据包 首先通过FFmpeg将一个媒体文件转推RTP&#xff0c;生成RTP流&#xff1a; ffmpeg -re -stream_loop -1 -i input.mp4 -vcodec copy -an -f rtp …