Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis 会话管理和token认证

Redis实现会话管理和token认证的示例代码

作者:昱晏

会话管理和身份认证是实现用户登录、权限管理等功能的基础,本文主就来介绍一下Redis实现会话管理和token认证的示例代码,具有一定的参考价值,感兴趣的可以了解一下

在现代Web应用中,会话管理身份认证是实现用户登录、权限管理等功能的基础。传统的会话管理通过服务器端保存会话信息来实现,但随着应用的扩展,尤其在分布式系统中,这种方式的局限性逐渐显现。Redis作为分布式缓存系统,具备高性能和高可用性,能够很好地解决分布式环境下的会话管理和Token认证问题。

本教程将介绍如何基于Redis和Spring Boot 实现会话管理与Token认证,确保应用在高并发、分布式架构中具备良好的性能和扩展性。

一、使用场景

二、原理解析

1. 会话管理

传统的会话管理通过在服务器端保存用户的会话状态(Session),并通过客户端(通常是浏览器)保存的Session ID与服务器进行匹配,来确定用户身份。在分布式环境下,本地Session机制无法保证跨实例共享,而Redis作为集中式存储,能够提供跨服务实例的会话共享机制。

2. Token认证

Token认证,尤其是基于JWT的认证方式,是一种无状态认证方案。与传统的Session机制不同,JWT将用户信息封装在Token中,发送给客户端,客户端在后续请求中携带该Token进行认证,服务器通过验证Token来确定用户身份。Redis可以用作存储Token的有效期或与其他用户数据的映射。

3. Redis在会话管理和Token认证中的角色

三、解决方案实现

1. 环境配置

首先,在pom.xml中添加Redis和Spring Security相关依赖:

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- Spring Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- JWT Token -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.2</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.2</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.2</version>
    </dependency>
</dependencies>

application.yml中配置Redis:

spring:
  redis:
    host: localhost
    port: 6379
    timeout: 6000ms

2. Redis会话管理实现

在Spring Boot中,我们可以通过Redis来管理会话信息,下面的示例代码展示如何使用Redis来存储用户会话信息。

配置Redis序列化器

为了使得对象能够存储在Redis中,我们需要配置Redis的序列化方式。

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 设置Key和Value的序列化器
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        
        return template;
    }
}

使用Redis存储Session信息

我们可以在用户登录后将会话信息存入Redis中。

@Service
public class SessionService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void saveSession(String sessionId, Object sessionData) {
        redisTemplate.opsForValue().set(sessionId, sessionData, 30, TimeUnit.MINUTES); // 会话有效期30分钟
    }

    public Object getSession(String sessionId) {
        return redisTemplate.opsForValue().get(sessionId);
    }

    public void deleteSession(String sessionId) {
        redisTemplate.delete(sessionId);
    }
}

3. Token认证实现

JWT生成与解析

JWT是无状态的认证方式,将用户信息封装在Token中,通过数字签名保证Token的安全性。我们使用jjwt库来生成和解析JWT。

JWT工具类

@Service
public class JwtTokenProvider {

    private static final String SECRET_KEY = "yourSecretKey";

    // 生成Token
    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // Token有效期1小时
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
                .compact();
    }

    // 解析Token
    public String getUsernameFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    // 验证Token是否过期
    public boolean isTokenExpired(String token) {
        Date expiration = Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody()
                .getExpiration();
        return expiration.before(new Date());
    }
}

JWT拦截器实现

为了在每次请求时验证Token的有效性,我们可以通过拦截器在请求到达控制器之前进行校验。

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = getTokenFromRequest(request);
        
        if (token != null && !jwtTokenProvider.isTokenExpired(token)) {
            String username = jwtTokenProvider.getUsernameFromToken(token);
            // 在SecurityContext中设置认证信息
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        
        filterChain.doFilter(request, response);
    }

    private String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

将拦截器添加到Spring Security配置中

我们需要将JwtAuthenticationFilter加入到Spring Security的过滤器链中。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/login", "/register").permitAll()  // 登录、注册请求不需要认证
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

4. Token与Redis的结合

为了进一步增强安全性,我们可以将生成的Token存储在Redis中,并设置一个过期时间。当Token失效或用户登出时,将其从Redis中移除。

@Service
public class TokenService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    public String createToken(String username) {
        String token = jwtTokenProvider.generateToken(username);
        redisTemplate.opsForValue().set(username, token, 1, TimeUnit.HOURS);  // Token存储在Redis中,1小时过期
        return token;
    }

    public boolean validateToken(String token) {
        String username = jwt
        String username = jwtTokenProvider.getUsernameFromToken(token);
        String redisToken = (String) redisTemplate.opsForValue().get(username);
        return token.equals(redisToken) &amp;&amp; !jwtTokenProvider.isTokenExpired(token);
    }

    public void invalidateToken(String username) {
        redisTemplate.delete(username);  // 从Redis中移除Token
    }
}

5. 登录接口实现

用户登录成功后,生成Token并存储到Redis中,同时将Token返回给客户端。客户端在后续的请求中携带此Token。

@RestController
@RequestMapping("/auth")
public class AuthController {

    @Autowired
    private TokenService tokenService;
    @Autowired
    private AuthenticationManager authenticationManager;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
        try {
            // 认证用户
            Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                    loginRequest.getUsername(), loginRequest.getPassword()));
            
            SecurityContextHolder.getContext().setAuthentication(authentication);
            
            // 生成Token并存储到Redis
            String token = tokenService.createToken(loginRequest.getUsername());
            
            return ResponseEntity.ok(new JwtResponse(token));
        } catch (AuthenticationException e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Authentication failed");
        }
    }

    @PostMapping("/logout")
    public ResponseEntity<?> logout(HttpServletRequest request) {
        String token = getTokenFromRequest(request);
        if (token != null) {
            String username = jwtTokenProvider.getUsernameFromToken(token);
            tokenService.invalidateToken(username);  // 从Redis中移除Token
        }
        return ResponseEntity.ok("Logout successful");
    }

    private String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

6. 请求流程示例

四、Redis会话管理与Token认证效果

五、总结

Redis不仅能解决分布式环境下会话共享的问题,也能通过高效存储和快速读取实现了Token认证的高性能处理。在Spring Boot 中,使用Redis与JWT结合的方案为分布式架构提供了强大的认证与授权支持。

到此这篇关于Redis实现会话管理和token认证的示例代码的文章就介绍到这了,更多相关Redis 会话管理和token认证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文