使用 Spring Boot 进行 JWT 身份验证
Posted
技术标签:
【中文标题】使用 Spring Boot 进行 JWT 身份验证【英文标题】:JWT Authentication with Springboot 【发布时间】:2018-08-29 05:47:35 【问题描述】:我正在使用 SpringBoot 开发具有微服务架构的 Rest Backend。为了保护端点,我使用了 JWT 令牌机制。我正在使用 Zuul API 网关。
如果请求具有所需的权限(来自 JWT 的 ROLE),它将被转发到正确的微服务。 Zuul api网关的“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");
这样我必须在这个类中编写每个请求授权部分。所以我希望通过“EnableGlobalMethodSecurity”来使用方法级别的安全性。
问题是我应该如何将这个安全机制与其他微服务连接起来。因为当我将 spring 安全依赖添加到其他微服务时,它们表现为不同的 spring 安全模块。我应该如何告诉其他使用 zuul server security 的微服务?
【问题讨论】:
【参考方案1】:首先(如果我理解正确的话)安全实现是在代理上吗?因为代理肯定只有两件事要做:过滤和路由……
我已实现的微服务应用程序流程如下图所示:
流程应该是这样的: https://www.rfc-editor.org/rfc/rfc6749#page-7
关于流程的简短介绍:
-
登录时您应该传递用户凭据
如果请求具有上下文路径“/security”(例如),您应该将请求重定向到 AuthServer(您决定安全实施)
如果用户传递可用的凭据,则 AuthServer 必须返回 access_token。
拥有访问令牌,用户可以向 AccountServices(资源服务)提出请求;
在 AccountServices 中,您必须实现一个配置类来解码 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());
关于公钥和私钥:私钥必须只有 AuthServer 知道,并且公钥必须在包括 AuthService 在内的任何服务中传递。您可以在此处生成公钥和私钥:http://travistidwell.com/jsencrypt/demo/ 并将这些密钥添加到 application.yml 文件中,并使用@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)创建一个配置类来解码access_token并检查用户是否有ROLE来做某事......这里您必须只传递公钥 以同样的方式 application.yml 文件。
关于@EnableGlobalMethodSecurity(prePostEnabled = true)
注解,您可以在控制器方法上添加@preauthorize
注解。
【讨论】:
以上是关于使用 Spring Boot 进行 JWT 身份验证的主要内容,如果未能解决你的问题,请参考以下文章
在 Spring Boot 中使用 JWT 进行简单的身份验证
Spring Boot OAuth2:从 cookie 中提取 JWT 进行身份验证
如何在 Spring Boot 中实现 oAuth2 和 JWT 身份验证? [关闭]