小能豆

Spring Security

javascript

我们系统要做统一登录,授权服务器跳转到我们系统之后,进行账号校验存在的话,直接进入系统吗?如果直接进入系统,redis中的token又是怎样存进去的?
我想的是我们系统内部应该要再登录一次,然后登录的话,需要账号密码,但是拿不到密码.只能新写一个登录接口,只需要账号就能登录.
新写登录接口,还得用Spring Security原有的登录逻辑,原来的登录和新的登录需要并存,目前情况就是这样,有没有什么思路


阅读 60

收藏
2024-06-02

共1个答案

小能豆

在设计一个统一登录系统时,尤其是结合 Spring Security 和 Redis 来管理 token,有几个关键点需要注意。你提到的问题涉及到如何在用户经过授权服务器跳转到系统后,不需要再次输入密码就能直接登录并存储 token。以下是一些详细的思路和解决方案。

思路概述

  1. 授权服务器(Auth Server)和资源服务器(Resource Server)分离
  2. 授权服务器负责用户认证和生成 token。
  3. 资源服务器负责保护资源,验证 token 的合法性。

  4. 使用 OAuth2/OpenID Connect

  5. 授权服务器生成并返回一个 JWT token,携带用户信息。
  6. 资源服务器接收并解析这个 token,验证用户身份。

  7. Spring Security 配置

  8. 资源服务器配置 Spring Security 以验证 token 的合法性。
  9. 在用户首次通过授权服务器登录后,将 token 存储到 Redis 中,管理用户会话。

详细步骤

1. 配置授权服务器

配置 OAuth2 授权服务器,生成并返回 JWT token。这部分的配置超出了当前问题的范围,可以参考 Spring Security OAuth2 的相关文档和教程。

2. 资源服务器接收并解析 JWT token

配置 Spring Security 以处理 JWT token 的验证和用户身份的管理。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer()
                .jwt(); // 配置使用 JWT 验证
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 配置自定义用户细节服务
    }
}

3. 使用 Redis 管理 Token

在用户首次通过授权服务器登录成功后,解析 JWT token 并将其存储到 Redis 中。

@Service
public class TokenService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public void storeToken(String username, String token) {
        redisTemplate.opsForValue().set(username, token);
        // 设置 token 的过期时间,保持与 JWT token 的有效期一致
    }

    public String getToken(String username) {
        return redisTemplate.opsForValue().get(username);
    }
}

4. 自定义登录接口

编写一个新的登录接口,只需要账号即可登录,并使用 Spring Security 的现有登录逻辑。

@RestController
public class CustomLoginController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenService tokenService;

    @PostMapping("/custom-login")
    public ResponseEntity<?> login(@RequestBody CustomLoginRequest request) {
        // 从请求中获取 JWT token
        String token = request.getToken();
        // 解析 JWT token 并获取用户信息
        String username = extractUsernameFromToken(token);

        // 创建一个 UsernamePasswordAuthenticationToken,只有用户名,没有密码
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());

        // 使用 AuthenticationManager 进行身份验证
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        // 将 token 存储到 Redis 中
        tokenService.storeToken(username, token);

        return ResponseEntity.ok("Login successful");
    }

    private String extractUsernameFromToken(String token) {
        // 解析 JWT token 获取用户名的逻辑
        // 使用 JWT 库,如 jjwt 或 Auth0 的 java-jwt
    }
}

5. 保持原有登录逻辑

确保新的登录接口与现有的登录逻辑并存,可以通过不同的 URL 映射来实现。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/login").permitAll()
            .antMatchers("/custom-login").permitAll()
            .anyRequest().authenticated()
        .and()
        .formLogin()
            .loginPage("/login")
            .defaultSuccessUrl("/home")
            .permitAll()
        .and()
        .logout()
            .permitAll()
        .and()
        .oauth2ResourceServer()
            .jwt();
}

总结

通过以上步骤,你可以实现一个统一登录系统,用户在授权服务器成功认证后,无需再次输入密码即可登录到你的系统。你可以通过解析 JWT token 获取用户信息,并将 token 存储到 Redis 中进行管理。同时,保留原有的登录逻辑,以确保新旧登录方式的并存和兼容。

2024-06-02