SpringSecurity + OAuth2限制一个用户登录

        系统中的一个小需求,一个用户在我的浏览器登录后,其他人用我的账号在别处登录,将我的浏览器的账号token做一个失效处理,达到只有一个用户登录的目的。

主要是利用redis去存储 用户id 和token的绑定

在登陆的时候根据id去覆盖这个token,就能实现在其他地方登陆后,原来的登陆位置token失效

1. 配置WebSecurityConfiguration


        主要是增加一个sessionManagement
        配置.maximumSessions(1) // 限制同一个用户只能有一个会话
        .maxSessionsPreventsLogin(false) //如果设置为true,当达到最大会话数时,拒绝新的登录

/**
 * 服务安全相关配置
 */
@EnableWebSecurity
public class WebSecurityConfiguration {

    /**
     * spring security 默认的安全策略
     * @param http security注入点
     * @return SecurityFilterChain
     * @throws Exception
     */
    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests(authorizeRequests -> authorizeRequests.antMatchers("/token/*")
                               .permitAll()// 开放自定义的部分端点
                               .anyRequest()
                               .authenticated())
        .headers()
        .frameOptions()
        .sameOrigin()// 避免iframe同源无法登录
        .and()
        .apply(new FormIdentityLoginConfigurer())// 表单登录个性化
        .and()
        .sessionManagement(sessionManagement -> sessionManagement
                           .maximumSessions(1) // 限制同一个用户只能有一个会话
                           .maxSessionsPreventsLogin(false) // 如果设置为true,当达到最大会话数时,拒绝新的登录
                          );
        // 处理 UsernamePasswordAuthenticationToken
        http.authenticationProvider(new PigDaoAuthenticationProvider());
        return http.build();
    }

    /**
     * 暴露静态资源
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    @Order(0)
    SecurityFilterChain resources(HttpSecurity http) throws Exception {
        // ...
        // 省略
    }

}

2.在实现了OAuth2AuthorizationService的类中


存储redis和利用redis做判断

@RequiredArgsConstructor
public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationService {

    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public void save(OAuth2Authorization authorization) {
        Assert.notNull(authorization, "authorization cannot be null");

        if (isState(authorization)) {
            // ....
            // 省略
        }

        if (isCode(authorization)) {
            // ....
            // 省略
        }

        if (isRefreshToken(authorization)) {
            // ....
            // 省略
        }

        if (isAccessToken(authorization)) {
            OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
            String userId = authorization.getPrincipalName(); // 假设用户ID是principalName
            // 1. 获取当前用户的旧token
            String oldTokenKey = (String) redisTemplate.opsForValue().get(USER_TOKEN + userId);

            // 2. 移除旧token的授权信息
            if (oldTokenKey != null && !oldTokenKey.equals(accessToken.getTokenValue())) {
                redisTemplate.delete(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, oldTokenKey));
            }

            // 3. 将新的 token 和用户ID关联存储
            redisTemplate.opsForValue().set(USER_TOKEN + userId, accessToken.getTokenValue());

            long between = ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt());
            redisTemplate.setValueSerializer(RedisSerializer.java());
            redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()),
                authorization, between, TimeUnit.SECONDS);
        }
    }

        private static boolean isAccessToken(OAuth2Authorization authorization) {
            return Objects.nonNull(authorization.getAccessToken());
        }
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值