MST

星途 面试题库

面试题:微服务架构下Spring Cloud分布式会话管理中,如何解决不同服务间会话一致性问题?

在Spring Cloud微服务架构中,各个服务可能部署在不同的服务器上,当用户的请求在多个服务间流转时,需要保证会话信息的一致性。请阐述你所知道的解决方法,并说明其中一种方法在Spring Cloud中的具体实现步骤。
23.0万 热度难度
后端开发微服务架构

知识考点

AI 面试

面试题答案

一键面试

解决方法

  1. 基于Cookie:在客户端和服务端之间通过Cookie传递会话信息。优点是简单直接,客户端可以直接携带;缺点是Cookie有大小限制,且安全性相对低,可能被篡改。
  2. 基于Token:服务端生成Token,客户端每次请求携带Token。Token中包含用户身份等会话相关信息。优点是无状态,便于分布式部署,可在不同服务间共享;缺点是如果Token过大,会增加传输开销。
  3. 使用分布式缓存:如Redis,将会话信息存储在Redis中,各个服务通过访问Redis获取和更新会话信息。优点是可扩展性强,数据一致性好;缺点是增加了系统复杂度,依赖Redis的稳定性。

基于Token在Spring Cloud中的具体实现步骤

  1. 引入依赖: 在项目的pom.xml中添加相关依赖,例如使用spring-boot-starter-securityjjwt(Java JWT库):
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<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>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
  1. 生成Token: 在用户登录成功后,生成Token。例如,创建一个JwtUtil工具类:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import java.security.Key;
import java.util.Date;

public class JwtUtil {
    private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    private static final long EXPIRATION_TIME = 10 * 60 * 1000; // 10分钟

    public static String generateToken(String username) {
        Date now = new Date();
        Date expiration = new Date(now.getTime() + EXPIRATION_TIME);

        Claims claims = Jwts.claims()
              .setSubject(username);
        claims.put("iat", now);
        claims.put("exp", expiration);

        return Jwts.builder()
              .setClaims(claims)
              .signWith(key, SignatureAlgorithm.HS256)
              .compact();
    }
}

在登录逻辑中调用JwtUtil.generateToken方法生成Token并返回给客户端。 3. 验证Token: 创建一个过滤器,例如JwtFilter,用于在请求进入服务时验证Token:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Key;

public class JwtFilter extends OncePerRequestFilter {
    private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    private final UserDetailsService userDetailsService;

    public JwtFilter(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);
            try {
                Claims claims = Jwts.parserBuilder()
                      .setSigningKey(key)
                      .build()
                      .parseClaimsJws(token)
                      .getBody();
                String username = claims.getSubject();
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            } catch (Exception e) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                return;
            }
        }
        filterChain.doFilter(request, response);
    }
}
  1. 配置过滤器: 在Spring Security配置类中注册JwtFilter
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final UserDetailsService userDetailsService;

    public SecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public JwtFilter jwtFilter() {
        return new JwtFilter(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
              .authorizeRequests()
              .antMatchers("/login").permitAll()
              .anyRequest().authenticated()
              .and()
              .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

这样,在Spring Cloud微服务中,通过Token实现了会话信息在多个服务间流转时的一致性。客户端每次请求携带Token,服务端通过验证Token获取会话信息,确保请求的合法性和会话一致性。