Spring Cloud Security是基于Spring Cloud的安全解决方案,它提供了很多功能模块,包括OAuth2、JWT、Session等。其中JWT(JSON Web Token)是一种轻量级的身份认证和授权机制,它是目前比较流行的一种Token机制,可以方便地实现无状态的身份认证和授权。
Spring Cloud Security中的JWT核心组件主要包括:
- JwtAuthenticationFilter:用于拦截请求并解析JWT Token,然后将用户信息设置到SecurityContext中;
- JwtTokenProvider:用于生成、解析和验证JWT Token;
- JwtConfig:用于配置JWT的相关参数,例如签名密钥、过期时间等。
下面我们将详细介绍如何在Spring Cloud Security中使用JWT。
1. 添加依赖
在使用Spring Cloud Security JWT之前,需要添加以下依赖:
代码语言:javascript复制<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
其中,spring-cloud-starter-security是Spring Cloud Security的基础依赖,jjwt是JWT的Java实现。
2. 配置JwtConfig
在使用JWT之前,需要配置JwtConfig,例如:
代码语言:javascript复制@Configuration
public class JwtConfig {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private int expiration;
public String getSecret() {
return secret;
}
public int getExpiration() {
return expiration;
}
}
其中,jwt.secret是JWT的签名密钥,jwt.expiration是JWT的过期时间(单位为秒)。
3. 实现JwtTokenProvider
接下来,需要实现JwtTokenProvider,例如:
代码语言:javascript复制@Component
public class JwtTokenProvider {
private final JwtConfig jwtConfig;
@Autowired
public JwtTokenProvider(JwtConfig jwtConfig) {
this.jwtConfig = jwtConfig;
}
public String generateToken(UserDetails userDetails) {
Date now = new Date();
Date expiration = new Date(now.getTime() jwtConfig.getExpiration() * 1000);
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret())
.compact();
}
public Authentication getAuthentication(String token) {
UserDetails userDetails = getUserDetails(token);
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
public UserDetails getUserDetails(String token) {
String username = getUsername(token);
return new User(username, "", new ArrayList<>());
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(jwtConfig.getSecret()).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
private String getUsername(String token) {
return Jwts.parser().setSigningKey(jwtConfig.getSecret()).parseClaimsJws(token).getBody().getSubject();
}
}
在上面的代码中,generateToken方法用于生成JWT Token,其中包括用户名、颁发时间、过期时间等信息;getAuthentication方法用于根据Token获取用户信息,并将其封装成Authentication对象;getUserDetails方法用于根据Token获取用户详细信息;validateToken方法用于验证Token是否有效;getUsername方法用于根据Token获取用户名。
需要注意的是,以上代码中的UserDetails、User等类需要根据实际情况进行修改。
4. 实现JwtAuthenticationFilter
接下来,需要实现JwtAuthenticationFilter,用于拦截请求并解析JWT Token,例如:
代码语言:javascript复制@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
@Autowired
public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = getToken(request);
if (StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String getToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
以上代码中的JwtAuthenticationFilter用于拦截请求,并从请求头中获取Token,然后根据Token获取用户信息并设置到SecurityContext中。
5. 配置WebSecurityConfigurerAdapter
最后,需要配置WebSecurityConfigurerAdapter,例如:
代码语言:javascript复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Autowired
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/authenticate").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
以上代码中的SecurityConfig继承自WebSecurityConfigurerAdapter,用于配置Spring Security的行为。在这里,我们将/api/authenticate接口设置为不需要认证,其他接口需要认证。同时,将JwtAuthenticationFilter添加到Spring Security的过滤器链中,用于拦截请求并进行身份认证。
6. 测试
最后,我们可以使用Postman等工具进行测试。首先,需要调用/authenticate接口进行身份认证,例如:
代码语言:javascript复制POST /api/authenticate HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Cache-Control: no-cache
{
"username": "admin",
"password": "123456"
}
如果身份认证成功,服务器将返回一个JWT Token,例如:
代码语言:javascript复制{
"token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTYxOTAxMDU0NSwiZXhwIjoxNjE5MDE0MTQ1fQ.h3x1foXYjxxzV85KM2F5R5_NyfE-MrjK9Xr5PhdRjvCUZ0l0CwzvmJlmyc8t3qyA68SlypjN9F2Gme8Iv7Q2w"
}
以上代码中的token就是生成的JWT Token,我们可以在后续请求中使用它来进行身份认证。
接下来,我们可以使用JWT Token进行身份认证,例如:
代码语言:javascript复制GET /api/users HTTP/1.1
Host: localhost:8080
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTYxOTAxMDU0NSwiZXhwIjoxNjE5MDE0MTQ1fQ.h3x1foXYjxxzV85KM2F5R5_NyfE-MrjK9Xr5PhdRjvCUZ0l0CwzvmJlmyc8t3qyA68SlypjN9F2Gme8Iv7Q2w
Cache-Control: no-cache
如果JWT Token有效,服务器将返回用户信息,例如:
代码语言:javascript复制[
{
"id": 1,
"username": "admin",
"password": "$2a$10$tGWfb3JyB1nS.8ETWfMyFeXGllq0Uh0jDslscfIMC.hgRt/Sz2tFe",
"email": "admin@example.com",
"roles": [
{
"id": 1,
"name": "ROLE_ADMIN"
}
]
},
{
"id": 2,
"username": "user",
"password": "$2a$10$wKgfnw1NfA.3vg.1Akrn3.xfb.lGKFDxvyF8n7rQm1LkLLtVl15Ky",
"email": "user@example.com",
"roles": [
{
"id": 2,
"name": "ROLE_USER"
}
]
}
]