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

 更新时间:2025年04月22日 09:54:50   作者:昱晏  
会话管理和身份认证是实现用户登录、权限管理等功能的基础,本文主就来介绍一下Redis实现会话管理和token认证的示例代码,具有一定的参考价值,感兴趣的可以了解一下

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

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

一、使用场景

  • 分布式系统:当系统部署在多个服务实例上时,服务器本地的Session无法跨实例共享,而Redis能作为集中式存储,帮助管理所有实例的会话信息。
  • 无状态认证:基于Token认证机制的实现,特别是JWT(JSON Web Token),适用于用户登录后通过Token进行认证,避免在每次请求时重新查询数据库或读取Session。
  • 高并发场景:在高并发的情况下,Redis的高吞吐量和低延迟能够保证会话管理和认证机制的高效性。

二、原理解析

1. 会话管理

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

2. Token认证

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

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

  • 会话管理:将用户的会话信息存储在Redis中,保证分布式系统中不同实例对会话的共享访问。
  • Token认证:存储Token的有效性和用户信息,或用于存储黑名单Token(已失效或已注销的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. 请求流程示例

  • 用户登录:用户提供用户名和密码,通过/auth/login接口进行登录。成功后,服务器生成JWT Token并存入Redis,并返回给客户端。
  • Token携带请求:客户端在后续的请求中,将Token放在Authorization头部中,发送到服务器。服务器在收到请求后,通过JWT解析Token,验证有效性。
  • 登出操作:用户在登出时,前端请求/auth/logout接口,服务器将用户的Token从Redis中移除,Token失效。

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

  • 高效性能:Redis的高并发读写能力保证了在高并发场景下的会话存储与Token验证的高效性。
  • 分布式支持:使用Redis作为集中存储,可以确保在多实例或分布式部署环境中共享会话数据,避免本地Session的局限性。
  • 安全性增强:通过Redis存储Token以及Token的有效期控制,可以快速实现Token的失效处理,增强了安全性。

五、总结

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

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

相关文章

  • 浅谈Redis的几个过期策略

    浅谈Redis的几个过期策略

    在使用redis时,一般会设置一个过期时间,当然也有不设置过期时间的,也就是永久不过期。当设置了过期时间,redis是如何判断是否过期,以及根据什么策略来进行删除的。
    2021-05-05
  • Redis关于内存碎片的解决方法

    Redis关于内存碎片的解决方法

    今天生产机报内存爆满异常被叫过去查看问题,通过各种排除最终定位到了Redis的内存碎片的问题,这篇博客将详细介绍Redis内存碎片问题并给出最佳实践解决此问题,需要的朋友可以参考下
    2024-07-07
  • Redis实现高并发计数器

    Redis实现高并发计数器

    这篇文章主要为大家详细介绍了Redis实现高并发计数器,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • redis-cli登录远程redis服务并批量导入数据

    redis-cli登录远程redis服务并批量导入数据

    本文主要介绍了redis-cli登录远程redis服务并批量导入数据,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-10-10
  • Redis中散列类型的常用命令小结

    Redis中散列类型的常用命令小结

    散列类型的键值其实也是一种字典解耦,其存储了字段和字段值的映射,但字段值只能是字符串,不支持其他数据类型,所以说散列类型不能嵌套其他的数据类型。下面就来详细介绍下Redis中散列类型的常用命令,有需要的可以参考学习。
    2016-09-09
  • Redis实现附近商铺的项目实战

    Redis实现附近商铺的项目实战

    本文主要介绍了Redis实现附近商铺的项目实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • 基于redis实现token验证用户是否登陆

    基于redis实现token验证用户是否登陆

    这篇文章主要为大家详细介绍了基于redis实现token验证用户是否登陆,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • redis requires ruby version2.2.2的解决方案

    redis requires ruby version2.2.2的解决方案

    本文主要介绍了redis requires ruby version2.2.2的解决方案,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • Redis安全策略详解

    Redis安全策略详解

    缓存穿透是指当用户在查询一条数据的时候,而此时数据库和缓存却没有关于这条数据的任何记录,而这条数据在缓存中没找到就会向数据库请求获取数据。用户拿不到数据时,就会一直发请求,查询数据库,这样会对数据库的访问造成很大的压力
    2022-07-07
  • Redis 哨兵机制及配置实现

    Redis 哨兵机制及配置实现

    本文主要介绍了Redis 哨兵机制及配置实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03

最新评论