面试题答案
一键面试解决方法
- 基于Cookie:在客户端和服务端之间通过Cookie传递会话信息。优点是简单直接,客户端可以直接携带;缺点是Cookie有大小限制,且安全性相对低,可能被篡改。
- 基于Token:服务端生成Token,客户端每次请求携带Token。Token中包含用户身份等会话相关信息。优点是无状态,便于分布式部署,可在不同服务间共享;缺点是如果Token过大,会增加传输开销。
- 使用分布式缓存:如Redis,将会话信息存储在Redis中,各个服务通过访问Redis获取和更新会话信息。优点是可扩展性强,数据一致性好;缺点是增加了系统复杂度,依赖Redis的稳定性。
基于Token在Spring Cloud中的具体实现步骤
- 引入依赖:
在项目的
pom.xml
中添加相关依赖,例如使用spring-boot-starter-security
和jjwt
(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>
- 生成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);
}
}
- 配置过滤器:
在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获取会话信息,确保请求的合法性和会话一致性。