Spring Boot 如何在 Zuul 微服务上验证令牌
Springboot how to validate token on Zuul microservice
我是 Springboot 的新手,我试图通过 Zuul API 网关过滤请求,但是我收到以下错误:
AnonymousAuthenticationToken cannot be cast to org.aacctt.ms.auth.security.JWTAuthentication
当我放置断点时,当请求从 zuul 网关到达身份验证服务时,我得到一个空 header/token 字符串值,这发生在需要授权令牌的受保护请求上。
我的目标是能够验证客户端发送的令牌,以便我可以允许客户端对受保护端点的请求或拒绝它。
我不确定我做错了什么是我的代码:
授权服务
@Component
public class JWTAuthorizationFilter extends GenericFilterBean {
private static final Logger LOG = LoggerFactory.getLogger(JWTAuthorizationFilter.class);
private static final String HEADER_STRING = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
@Value("${jwt.encryption.secret}")
private String SECRET;
@Value("${jwt.access.token.expiration.seconds}")
private long EXPIRATION_TIME_IN_SECONDS;
public String generateAccessToken(long userId) {
return JWT.create()
.withSubject(String.valueOf(userId))
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME_IN_SECONDS * 1000))
.sign(Algorithm.HMAC256(SECRET.getBytes()));
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String header = httpRequest.getHeader(HEADER_STRING); // this is null
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(httpRequest, httpResponse);
return;
}
SecurityContextHolder.getContext().setAuthentication(getAuthentication(header));
chain.doFilter(httpRequest, httpResponse);
}
private Authentication getAuthentication(String token) {
final String username;
try {
DecodedJWT jwt = JWT.require(Algorithm.HMAC256(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""));
username = jwt.getSubject();
} catch (JWTVerificationException e) {
LOG.debug("Invalid JWT", e);
return null;
}
final Long userId;
try {
userId = Long.valueOf(username);
} catch (NumberFormatException e) {
LOG.debug("Invalid JWT. Username is not an user ID");
return null;
}
LOG.debug("Valid JWT. User ID: " + userId);
return new JWTAuthentication(userId);
}
}
WebSecurityConfig
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final JWTAuthorizationFilter jwtAuthorizationFilter;
public WebSecurityConfig(JWTAuthorizationFilter jwtAuthorizationFilter) {
this.jwtAuthorizationFilter = jwtAuthorizationFilter;
}
@Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().disable();
http.csrf().disable();
http.addFilterAfter(jwtAuthorizationFilter, BasicAuthenticationFilter.class);
http.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers(AccountController.PATH_POST_SIGN_UP).permitAll()
.antMatchers(AccountController.PATH_POST_REFRESH).permitAll()
.antMatchers(AccountController.PATH_POST_LOGIN).permitAll()
.antMatchers("/v2/api-docs",
"/swagger-resources/configuration/ui",
"/swagger-resources",
"/swagger-resources/configuration/security",
"/swagger-ui.html",
"/webjars/**").permitAll()
.anyRequest().authenticated()
;
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
JWTAuthentication
public class JWTAuthentication implements Authentication {
private final long userId;
public JWTAuthentication(long userId) {
this.userId = userId;
}
@Override public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptySet();
}
@Override public Object getCredentials() {
return null;
}
@Override public Object getDetails() {
return null;
}
public long getUserId() {
return userId;
}
@Override public Long getPrincipal() {
return userId;
}
@Override public boolean isAuthenticated() {
return true;
}
@Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
throw new UnsupportedOperationException("JWT authentication is always authenticated");
}
@Override public String getName() {
return String.valueOf(userId);
}
}
安全服务
@Service
public class SecurityService {
public long getLoggedUserId() {
JWTAuthentication authentication = (JWTAuthentication) SecurityContextHolder.getContext().getAuthentication();
return authentication.getUserId();
}
}
Zuul 网关
public class AuthorizationFilter extends BasicAuthenticationFilter {
private static final Logger LOG = LoggerFactory.getLogger(AuthorizationFilter.class);
private static final String HEADER_STRING = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
Environment environment;
public AuthorizationFilter(AuthenticationManager authManager, Environment environment) {
super(authManager);
this.environment = environment;
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String authorizationHeader = req.getHeader(environment.getProperty("authorization.token.header.name"));
if (authorizationHeader == null || !authorizationHeader.startsWith(environment.getProperty("authorization.token.header.prefix"))) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req) {
String token = req.getHeader(HEADER_STRING);
final String username;
try {
DecodedJWT jwt = JWT.require(Algorithm.HMAC256(environment.getProperty("token.secret").getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""));
username = jwt.getSubject();
} catch (JWTVerificationException e) {
LOG.debug("Invalid JWT", e);
return null;
}
final Long userId;
try {
userId = Long.valueOf(username);
} catch (NumberFormatException e) {
LOG.debug("Invalid JWT. Username is not an user ID");
return null;
}
LOG.debug("Valid JWT. User ID: " + userId);
return new UsernamePasswordAuthenticationToken(userId, null, new ArrayList<>());
}
}
问题是敏感的header,Authorization
在Zuul中默认是敏感的header,你只需要覆盖敏感的headers。
zuul:
sensitive-headers:
-
通过在 Zuul 网关 application.yml 中设置此 属性 它将请求路由到具有 Authorization
header
的身份验证服务
仅供参考:
Auth Service reference
基于 JWT 的身份验证
https://github.com/nikhilmalavia/SpringBootJWT.git
我是 Springboot 的新手,我试图通过 Zuul API 网关过滤请求,但是我收到以下错误:
AnonymousAuthenticationToken cannot be cast to org.aacctt.ms.auth.security.JWTAuthentication
当我放置断点时,当请求从 zuul 网关到达身份验证服务时,我得到一个空 header/token 字符串值,这发生在需要授权令牌的受保护请求上。
我的目标是能够验证客户端发送的令牌,以便我可以允许客户端对受保护端点的请求或拒绝它。
我不确定我做错了什么是我的代码:
授权服务
@Component
public class JWTAuthorizationFilter extends GenericFilterBean {
private static final Logger LOG = LoggerFactory.getLogger(JWTAuthorizationFilter.class);
private static final String HEADER_STRING = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
@Value("${jwt.encryption.secret}")
private String SECRET;
@Value("${jwt.access.token.expiration.seconds}")
private long EXPIRATION_TIME_IN_SECONDS;
public String generateAccessToken(long userId) {
return JWT.create()
.withSubject(String.valueOf(userId))
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME_IN_SECONDS * 1000))
.sign(Algorithm.HMAC256(SECRET.getBytes()));
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String header = httpRequest.getHeader(HEADER_STRING); // this is null
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(httpRequest, httpResponse);
return;
}
SecurityContextHolder.getContext().setAuthentication(getAuthentication(header));
chain.doFilter(httpRequest, httpResponse);
}
private Authentication getAuthentication(String token) {
final String username;
try {
DecodedJWT jwt = JWT.require(Algorithm.HMAC256(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""));
username = jwt.getSubject();
} catch (JWTVerificationException e) {
LOG.debug("Invalid JWT", e);
return null;
}
final Long userId;
try {
userId = Long.valueOf(username);
} catch (NumberFormatException e) {
LOG.debug("Invalid JWT. Username is not an user ID");
return null;
}
LOG.debug("Valid JWT. User ID: " + userId);
return new JWTAuthentication(userId);
}
}
WebSecurityConfig
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final JWTAuthorizationFilter jwtAuthorizationFilter;
public WebSecurityConfig(JWTAuthorizationFilter jwtAuthorizationFilter) {
this.jwtAuthorizationFilter = jwtAuthorizationFilter;
}
@Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().disable();
http.csrf().disable();
http.addFilterAfter(jwtAuthorizationFilter, BasicAuthenticationFilter.class);
http.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers(AccountController.PATH_POST_SIGN_UP).permitAll()
.antMatchers(AccountController.PATH_POST_REFRESH).permitAll()
.antMatchers(AccountController.PATH_POST_LOGIN).permitAll()
.antMatchers("/v2/api-docs",
"/swagger-resources/configuration/ui",
"/swagger-resources",
"/swagger-resources/configuration/security",
"/swagger-ui.html",
"/webjars/**").permitAll()
.anyRequest().authenticated()
;
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
JWTAuthentication
public class JWTAuthentication implements Authentication {
private final long userId;
public JWTAuthentication(long userId) {
this.userId = userId;
}
@Override public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptySet();
}
@Override public Object getCredentials() {
return null;
}
@Override public Object getDetails() {
return null;
}
public long getUserId() {
return userId;
}
@Override public Long getPrincipal() {
return userId;
}
@Override public boolean isAuthenticated() {
return true;
}
@Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
throw new UnsupportedOperationException("JWT authentication is always authenticated");
}
@Override public String getName() {
return String.valueOf(userId);
}
}
安全服务
@Service
public class SecurityService {
public long getLoggedUserId() {
JWTAuthentication authentication = (JWTAuthentication) SecurityContextHolder.getContext().getAuthentication();
return authentication.getUserId();
}
}
Zuul 网关
public class AuthorizationFilter extends BasicAuthenticationFilter {
private static final Logger LOG = LoggerFactory.getLogger(AuthorizationFilter.class);
private static final String HEADER_STRING = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
Environment environment;
public AuthorizationFilter(AuthenticationManager authManager, Environment environment) {
super(authManager);
this.environment = environment;
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String authorizationHeader = req.getHeader(environment.getProperty("authorization.token.header.name"));
if (authorizationHeader == null || !authorizationHeader.startsWith(environment.getProperty("authorization.token.header.prefix"))) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req) {
String token = req.getHeader(HEADER_STRING);
final String username;
try {
DecodedJWT jwt = JWT.require(Algorithm.HMAC256(environment.getProperty("token.secret").getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""));
username = jwt.getSubject();
} catch (JWTVerificationException e) {
LOG.debug("Invalid JWT", e);
return null;
}
final Long userId;
try {
userId = Long.valueOf(username);
} catch (NumberFormatException e) {
LOG.debug("Invalid JWT. Username is not an user ID");
return null;
}
LOG.debug("Valid JWT. User ID: " + userId);
return new UsernamePasswordAuthenticationToken(userId, null, new ArrayList<>());
}
}
问题是敏感的header,Authorization
在Zuul中默认是敏感的header,你只需要覆盖敏感的headers。
zuul:
sensitive-headers:
-
通过在 Zuul 网关 application.yml 中设置此 属性 它将请求路由到具有 Authorization
header
仅供参考:
Auth Service reference
基于 JWT 的身份验证 https://github.com/nikhilmalavia/SpringBootJWT.git