使用 Spring Boot 进行 JWT 身份验证
JWT Authentication with Springboot
我正在使用 SpringBoot 开发具有微服务架构的 Rest Backend。为了保护端点,我使用了 JWT 令牌机制。我正在使用 Zuul API 网关。
如果请求需要权限(来自 JWT 的 ROLE),它将被转发到正确的微服务。 Zuulapi网关的"WebSecurityConfigurerAdapter"如下
@Autowired
private JwtAuthenticationConfig config;
@Bean
public JwtAuthenticationConfig jwtConfig() {
return new JwtAuthenticationConfig();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.logout().disable()
.formLogin().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.anonymous()
.and()
.exceptionHandling().authenticationEntryPoint(
(req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.addFilterAfter(new JwtTokenAuthenticationFilter(config),
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(config.getUrl()).permitAll()
.antMatchers("/api/user/**").permitAll()
.antMatchers("/api/package/**").hasRole("USER")
.antMatchers("/api/dashboard/**").hasRole("USER")
.antMatchers("/api/records/**").hasRole("USER");
}
这样我就得把每一个请求授权部分都写在这个class里面了。所以我希望使用方法级别的安全性,"EnableGlobalMethodSecurity"。
问题是我应该如何将此安全机制与其他微服务连接起来。因为当我将 spring 安全依赖项添加到其他微服务时,它们表现为不同的 spring 安全模块。我应该如何告知其他使用 zuul 服务器安全性的微服务?
首先(如果我理解正确的话)安全实施是在代理上吗?因为代理必须只有两件事要做:过滤和路由...
我实现的微服务应用程序流程如下图所示:
流程应该是这样的:
https://www.rfc-editor.org/rfc/rfc6749#page-7
流程简介:
- 登录时您应该传递用户凭据
- 如果请求有上下文路径“/security”(例如),您应该将请求重定向到 AuthServer(您决定安全实现)
- 如果用户传递可用凭据,AuthServer 必须 return 一个 access_token。
- 拥有访问令牌的用户能够向 AccountServices(资源服务)发出请求;
在 AccountServices 中,您必须实施配置 class 来解码 access_token 并检查用户是否有权访问所请求的资源
关于 Spring 中实现的 OAuth2 框架,您还可以在这里找到一个很好的文档:http://projects.spring.io/spring-security-oauth/docs/oauth2.html
部分代码:
在 AuthService 上
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
public final static String RESOURCE_ID = "server-resource";
@Value("${jwt.publicKey}")
private String publicKey;
@Value("${jwt.privateKey}")
private String privateKey;
@Autowired
private AuthenticationManager authenticationManager;
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey(publicKey);
converter.setSigningKey(privateKey);
return converter;
}
@Bean
public TokenEnhancer customTokenEnhancer() {
return new CustomTokenEnhancer();
}
@Override
public void configure(ClientDetailsServiceConfigurer client) throws Exception {
client.inMemory()
.withClient("client")
.secret("clientsecret")
.scopes("read", "write")
.resourceIds("user")
.authorizedGrantTypes("password", "refresh_token", "authorization_code")
.authorities("ROLE_TRUSTED_CLIENT")
.accessTokenValiditySeconds(tokenExpire) // one day available
.refreshTokenValiditySeconds(refreshExpire);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer server) throws Exception {
server
.tokenKeyAccess("hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.accessTokenConverter(accessTokenConverter());
}
}
关于public和私钥: 私钥必须只有AuthServer知道,public 密钥必须在任何服务中传递,包括 AuthService。您可以在此处生成 public 和私钥:http://travistidwell.com/jsencrypt/demo/ 并将这些密钥添加到 application.yml 文件中并传递到配置 class @Value
.
在资源服务器上
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Value("${jwt.publicKey}")
private String publicKey;
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
protected JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey(publicKey);
return converter;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.tokenStore(tokenStore())
.resourceId("user");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().antMatchers("/**").permitAll();
}
}
您唯一需要做的就是为资源服务 (AccountService) 创建一个配置 class 来解码 access_token 并检查用户是否有权限做某事...这里您必须以相同的方式仅传递 public 键 application.yml 文件。
关于 @EnableGlobalMethodSecurity(prePostEnabled = true)
注释,您可以在控制器方法上添加 @preauthorize
注释。
我正在使用 SpringBoot 开发具有微服务架构的 Rest Backend。为了保护端点,我使用了 JWT 令牌机制。我正在使用 Zuul API 网关。
如果请求需要权限(来自 JWT 的 ROLE),它将被转发到正确的微服务。 Zuulapi网关的"WebSecurityConfigurerAdapter"如下
@Autowired
private JwtAuthenticationConfig config;
@Bean
public JwtAuthenticationConfig jwtConfig() {
return new JwtAuthenticationConfig();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.logout().disable()
.formLogin().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.anonymous()
.and()
.exceptionHandling().authenticationEntryPoint(
(req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.addFilterAfter(new JwtTokenAuthenticationFilter(config),
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(config.getUrl()).permitAll()
.antMatchers("/api/user/**").permitAll()
.antMatchers("/api/package/**").hasRole("USER")
.antMatchers("/api/dashboard/**").hasRole("USER")
.antMatchers("/api/records/**").hasRole("USER");
}
这样我就得把每一个请求授权部分都写在这个class里面了。所以我希望使用方法级别的安全性,"EnableGlobalMethodSecurity"。
问题是我应该如何将此安全机制与其他微服务连接起来。因为当我将 spring 安全依赖项添加到其他微服务时,它们表现为不同的 spring 安全模块。我应该如何告知其他使用 zuul 服务器安全性的微服务?
首先(如果我理解正确的话)安全实施是在代理上吗?因为代理必须只有两件事要做:过滤和路由...
我实现的微服务应用程序流程如下图所示:
流程应该是这样的: https://www.rfc-editor.org/rfc/rfc6749#page-7
流程简介:
- 登录时您应该传递用户凭据
- 如果请求有上下文路径“/security”(例如),您应该将请求重定向到 AuthServer(您决定安全实现)
- 如果用户传递可用凭据,AuthServer 必须 return 一个 access_token。
- 拥有访问令牌的用户能够向 AccountServices(资源服务)发出请求;
在 AccountServices 中,您必须实施配置 class 来解码 access_token 并检查用户是否有权访问所请求的资源
关于 Spring 中实现的 OAuth2 框架,您还可以在这里找到一个很好的文档:http://projects.spring.io/spring-security-oauth/docs/oauth2.html
部分代码:
在 AuthService 上
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { public final static String RESOURCE_ID = "server-resource"; @Value("${jwt.publicKey}") private String publicKey; @Value("${jwt.privateKey}") private String privateKey; @Autowired private AuthenticationManager authenticationManager; @Bean public TokenStore tokenStore() { return new JwtTokenStore(accessTokenConverter()); } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setVerifierKey(publicKey); converter.setSigningKey(privateKey); return converter; } @Bean public TokenEnhancer customTokenEnhancer() { return new CustomTokenEnhancer(); } @Override public void configure(ClientDetailsServiceConfigurer client) throws Exception { client.inMemory() .withClient("client") .secret("clientsecret") .scopes("read", "write") .resourceIds("user") .authorizedGrantTypes("password", "refresh_token", "authorization_code") .authorities("ROLE_TRUSTED_CLIENT") .accessTokenValiditySeconds(tokenExpire) // one day available .refreshTokenValiditySeconds(refreshExpire); } @Override public void configure(AuthorizationServerSecurityConfigurer server) throws Exception { server .tokenKeyAccess("hasAuthority('ROLE_TRUSTED_CLIENT')") .checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(tokenStore()) .authenticationManager(authenticationManager) .accessTokenConverter(accessTokenConverter()); } }
关于public和私钥: 私钥必须只有AuthServer知道,public 密钥必须在任何服务中传递,包括 AuthService。您可以在此处生成 public 和私钥:http://travistidwell.com/jsencrypt/demo/ 并将这些密钥添加到 application.yml 文件中并传递到配置 class @Value
.
在资源服务器上
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class OAuth2ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Value("${jwt.publicKey}") private String publicKey; @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean protected JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setVerifierKey(publicKey); return converter; } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources .tokenStore(tokenStore()) .resourceId("user"); } @Override public void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests().antMatchers("/**").permitAll(); } }
您唯一需要做的就是为资源服务 (AccountService) 创建一个配置 class 来解码 access_token 并检查用户是否有权限做某事...这里您必须以相同的方式仅传递 public 键 application.yml 文件。
关于 @EnableGlobalMethodSecurity(prePostEnabled = true)
注释,您可以在控制器方法上添加 @preauthorize
注释。