JWT(JSON Web Token)是一种允许在网络应用之间传递声明的开放标准。它可以通过签名保证数据的完整性,并建立信任关系,因此在身份验证和授权方面非常有用。在Spring Security框架中,我们可以使用JWT作为OAuth2的Token Store。
以下是使用JWT作为Spring Security OAuth2的Token Store的攻略:
1. 引入依赖
在pom.xml文件中,我们需要引入spring-security-jwt和spring-security-oauth2的依赖:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.4.0.RELEASE</version>
</dependency>
2. 创建JWT加密/解密工具
下面是一个JWT加密/解密的工具类。
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Component
public class JwtUtils {
private final String SECRET_KEY = "secret";
public String generateJwtToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createJwtToken(claims, userDetails.getUsername());
}
private String createJwtToken(Map<String, Object> claims, String subject) {
Date now = new Date();
Date expiration = new Date(now.getTime() + 1000 * 60 * 60 * 10); // 10 hours
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
public Boolean validateJwtToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
private Boolean isTokenExpired(String token) {
final Date expiration = extractExpiration(token);
return expiration.before(new Date());
}
private String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
private Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
}
在这个工具类中,我们使用了io.jsonwebtoken包中的类。generateJwtToken()方法用于生成JWT,validateJwtToken()方法用于验证JWT是否有效。
3. 扩展AuthorizationServerConfigurerAdapter和ResourceServerConfigurerAdapter类
下面是在Spring Security中扩展AuthorizationServerConfigurerAdapter和ResourceServerConfigurerAdapter类的代码:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import javax.sql.DataSource;
@Configuration
public class SecurityConfig {
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final DataSource dataSource;
public AuthorizationServerConfig(AuthenticationManager authenticationManager, DataSource dataSource) {
this.authenticationManager = authenticationManager;
this.dataSource = dataSource;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(new JdbcClientDetailsService(dataSource));
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore());
}
private TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
private JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("secret");
return converter;
}
}
@Configuration
@EnableResourceServer
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private final TokenStore tokenStore;
public ResourceServerConfig(TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll();
}
}
@Configuration
protected static class AuthenticationManagerSetup extends AuthenticationManagerBuilderConfigurer {
private final DataSource dataSource;
public AuthenticationManagerSetup(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
}
@Configuration
protected static class AuthenticationManagerConfigurer {
private final DataSource dataSource;
public AuthenticationManagerConfigurer(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public JdbcUserDetailsService userDetailsService() {
JdbcUserDetailsService jdbcUserDetailsService = new JdbcUserDetailsService();
jdbcUserDetailsService.setDataSource(dataSource);
return jdbcUserDetailsService;
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager(Arrays.asList(databaseProvider()));
}
@Bean
DaoAuthenticationProvider databaseProvider() {
DaoAuthenticationProvider databaseProvider = new DaoAuthenticationProvider();
databaseProvider.setUserDetailsService(userDetailsService());
databaseProvider.setPasswordEncoder(passwordEncoder());
return databaseProvider;
}
@Bean(name = "passwordEncoder")
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
}
在这个配置文件中,我们创建了AuthorizationServerConfig和ResourceServerConfig类,用于配置授权服务器和资源服务器。在AuthorizationServerConfig类中,我们配置了客户端详情服务和授权服务器端点。在ResourceServerConfig类中,我们配置了资源服务器的保护规则和访问权限。在AuthenticationManagerSetup类中,我们配置了认证管理器的用户详细信息。在AuthenticationManagerConfigurer类中,我们为用户密码提供了一个BCryptPasswordEncoder方法。
示例1:生成JWT
下面是使用JwtUtils生成JWT的示例代码:
public class Main {
public static void main(String[] args) throws Exception {
JwtUtils jwtUtils = new JwtUtils();
UserDetails userDetails = new User("username", "password", new ArrayList<>());
String jwtToken = jwtUtils.generateJwtToken(userDetails);
System.out.println(jwtToken);
}
}
示例2:验证JWT
下面是使用JwtUtils验证JWT是否有效的示例代码:
@Test
public void givenValidToken_whenValidate_thenSuccess() {
JwtUtils jwtUtils = new JwtUtils();
UserDetails userDetails = new User("username", "password", new ArrayList<>());
String jwtToken = jwtUtils.generateJwtToken(userDetails);
assertTrue(jwtUtils.validateJwtToken(jwtToken, userDetails));
}
这里我们首先生成JWT Token,然后使用JwtUtils类验证这个Token是否有效。如果Token有效,那么测试将会通过。
这就是使用JWT作为Spring Security OAuth2的Token Store的完整攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:使用JWT作为Spring Security OAuth2的token存储问题 - Python技术站