SpringSecurity Oauth2 - 访问令牌续期

文章目录

    • [1. 访问令牌的续期](#1. 访问令牌的续期)
    • [2. CustomUserDetailsService](#2. CustomUserDetailsService)
    • [3. 配置 AuthorizationServerEndpointsConfigurer](#3. 配置 AuthorizationServerEndpointsConfigurer)
    • [4. 测试项目](#4. 测试项目)

1. 访问令牌的续期

在Spring Security OAuth2中,访问令牌的续期通常是通过使用**刷新令牌(Refresh Token)**来实现的。当访问令牌过期时,客户端可以使用之前获取的刷新令牌来获取新的访问令牌,而不需要再次请求用户认证。

访问令牌续期的基本流程:

获取刷新令牌:当客户端第一次请求访问令牌时(例如通过授权码模式或密码模式),可以同时获取一个刷新令牌。这个刷新令牌可以在访问令牌过期后用于请求新的访问令牌。

请求新的访问令牌:当访问令牌过期时,客户端可以通过向授权服务器发送一个带有刷新令牌的请求来获取新的访问令牌。

复制代码
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token={刷新令牌}&client_id={客户端ID}&client_secret={客户端密钥}

关键参数:

grant_type : 需要设置为 refresh_token,表示这是一个刷新令牌请求。

refresh_token: 客户端在最初请求访问令牌时获取的刷新令牌。

client_id: 客户端ID,用于标识客户端。

client_secret: 客户端密钥,用于验证客户端的身份。

刷新令牌的安全性:刷新令牌通常比访问令牌具有更长的有效期,因此需要更严格的保护。可以考虑使用HTTPS来传输刷新令牌,并确保客户端的安全性。

刷新令牌的撤销:如果用户注销或改变密码,通常需要撤销刷新令牌以防止继续使用。

单次使用刷新令牌:一些实现会确保刷新令牌只能使用一次,每次使用后生成新的刷新令牌以增强安全性。

在Spring Security OAuth2中,自定义UserDetailsService可以帮助你在处理访问令牌续期时,增加对用户信息的自定义检查或逻辑。例如,你可以在续期访问令牌时检查用户状态是否有效,或在认证过程中引入更多的自定义用户逻辑。

2. CustomUserDetailsService

自定义一个UserDetailsService,用于加载用户特定的数据。这个服务会在用户进行身份验证或令牌续期时被调用。

java 复制代码
@Service
public class CustomUserDetailService implements UserDetailsService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private PolicyDao policyDao;

    @Autowired
    private RoleDao roleDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 从数据库中查找用户
        UserEntity userEntity = userDao.queryUserByUserName(username);
        if (userEntity == null) {
            throw new UsernameNotFoundException("User not found with username: " + username);
        }
        // 根据用户信息查询角色信息
        List<RoleEntity> roleEntities = roleDao.queryRolesByUserId(userEntity.getId());
        List<String> roleIds = roleEntities.stream().map(RoleEntity::getId).collect(Collectors.toList());
        // 根据角色信息查询权限信息
        List<PolicyEntity> policyEntities = policyDao.queryPolicyByRoleId(roleIds);
        // 查询权限名称
        List<String> policyNames = policyEntities.stream().map(PolicyEntity::getName).collect(Collectors.toList());

        // 构造认证用户权限信息
        List<SimpleGrantedAuthority> grantedAuthorities
                = policyNames.stream().map(policyName -> new SimpleGrantedAuthority(policyName)).collect(Collectors.toList());

        // 将 UserEntity 转换为 UserDetails 对象
        UserDetails userDetails = User.builder()
                .username(userEntity.getUsername())
                .password(userEntity.getPassword())
                .authorities(grantedAuthorities)
                .accountExpired(false)
                .accountLocked(false)
                .disabled(false)
                .build();
        return userDetails ;
    }
}

3. 配置 AuthorizationServerEndpointsConfigurer

将自定义的 UserDetailsService 注册到 Spring Security OAuth2 的授权服务器中:

java 复制代码
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private CustomUserDetailService customUserDetailService;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 用于配置授权服务器的安全性,如 /oauth/token、/oauth/authorize 等端点的安全性配置。
        // 允许客户端表单身份验证
       security.allowFormAuthenticationForClients()
               // 允许所有人访问令牌验证端点
               .checkTokenAccess("permitAll()")
               // 仅允许认证后的用户访问密钥端点
               .tokenKeyAccess("isAuthenticated");
    }

    /**
     * 对于每个客户端应用,授权服务器会为其分配一个唯一的客户端ID和客户端密钥,并定义其授权范围和访问权限。
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 用于配置客户端详细信息服务,这个服务用来定义哪些客户端可以访问授权服务器以及客户端的配置信息。
        // 将客户端信息存储在内存中,适合开发和测试环境。
        clients.inMemory()
                // 定义客户端ID
                .withClient("client_id")
                // 定义客户端密钥
                .secret(passwordEncoder.encode("client_secret"))
                // 定义客户端支持的授权模式。
                .authorizedGrantTypes("password","refresh_token","client_credentials")
                // 设置访问令牌的有效期。
                .accessTokenValiditySeconds(3600)
                // 设置刷新令牌的有效期。
                .refreshTokenValiditySeconds(7200)
                // 定义客户端的作用范围。
                .scopes("all");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 用于配置授权和令牌的端点,以及令牌服务、令牌存储、用户认证等相关配置。
        // 配置用于密码模式的 AuthenticationManager。
        endpoints.authenticationManager(authenticationManager)
                // 在刷新令牌时使用此服务加载用户信息。
                .userDetailsService(customUserDetailService)
                // 配置令牌的存储策略,例如内存、数据库或 Redis。
                .tokenStore(tokenStore);
    }
}

4. 测试项目

① 获取访问令牌:

② 当访问令牌过期时,客户端可以通过向授权服务器发送一个带有刷新令牌的请求来获取新的访问令牌。

相关推荐
莫魂魂13 分钟前
002_IDEA企业开发工具
java·ide·intellij-idea
〆、风神31 分钟前
EasyExcel 数据字典转换器实战:注解驱动设计
android·java·注解
Thanwind32 分钟前
关于JVM和OS中的指令重排以及JIT优化
java·jvm·jmm
程序猿阿伟33 分钟前
《打破SQL与AI框架对接壁垒,解锁融合新路径》
数据库·人工智能·sql
点燃大海36 分钟前
MySQL表结构导出(Excel)
java·数据库·python·mysql·spring
꧁坚持很酷꧂1 小时前
Qt远程连接数据库,注册,登录
开发语言·数据库·qt
加油,旭杏1 小时前
【Redis】服务端高并发分布式结构
数据库·redis·分布式
倒霉蛋小马2 小时前
【Java集合】LinkedList源码深度分析
java·开发语言
风象南2 小时前
SpringBoot中6种API版本控制策略
java·spring boot·后端
꧁༺朝花夕逝༻꧂2 小时前
MySQL--数据备份
数据库·mysql