应用安全筛选器后,Zuul Routing不会重定向到给定的URL
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了应用安全筛选器后,Zuul Routing不会重定向到给定的URL相关的知识,希望对你有一定的参考价值。
我有一个spring boot应用程序,它使用spring security和spring zuul路由和过滤器。我有2个请求,一个是/ auth,为安全性而忽略了。另一个是/ api,它使用自定义过滤器进行JWT身份验证。当前的问题是第二个请求/ api没有路由到属性文件中提到的相应服务。
这是我的安全配置类
@EnableWebSecurity
@Configuration
public class WebSecurityConfigurtion extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAuthenticationEntryPoinit customAuthEntrypoint;
@Autowired
private JwtConfig jwtConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(customAuthEntrypoint)
.and()
.authorizeRequests()
.antMatchers("/api/**").permitAll()
.antMatchers("/auth/**").permitAll()
.and()
.antMatcher("/api/**") // if we mention the antmatcher first , it will apply only if the url starts with /api
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.addFilterBefore(jwtTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public JwtTokenAuthenticationFilter jwtTokenAuthenticationFilter() {
return new JwtTokenAuthenticationFilter(jwtConfig);
}
@Bean
public FilterRegistrationBean registration(JwtTokenAuthenticationFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/auth/**");//ignore all /auth request , so no security will be applied , so it wont check for matching filters for /auth
}
路由属性
zuul.routes.auth-service.path=/auth/**
zuul.routes.auth-service.url=http://localhost:9100/
zuul.routes.auth-service.strip-prefix=false
zuul.routes.userservice.path=/api/userservice/**
zuul.routes.userservice.url=http://localhost:9200/
zuul.routes.userservice.strip-prefix=false
请参阅自定义JWTAuthFilter
package com.cavion.config;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
public class JwtTokenAuthenticationFilter extends OncePerRequestFilter {
private final JwtConfig jwtConfig;
public JwtTokenAuthenticationFilter(JwtConfig jwtConfig) {
this.jwtConfig = jwtConfig;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
// 1. get the authentication header. Tokens are supposed to be passed in the
// authentication header
System.out.println("am in JwtTokenAuthenticationFilter");
String header = request.getHeader(jwtConfig.getHeader());
// 2. validate the header and check the prefix
if (header == null || !header.startsWith(jwtConfig.getPrefix())) {
chain.doFilter(request, response); // If not valid, go to the next filter.
return;
}
// If there is no token provided and hence the user won't be authenticated.
// It's Ok. Maybe the user accessing a public path or asking for a token.
// All secured paths that needs a token are already defined and secured in
// config class.
// And If user tried to access without access token, then he won't be
// authenticated and an exception will be thrown.
// 3. Get the token
String token = header.replace(jwtConfig.getPrefix(), "");
try { // exceptions might be thrown in creating the claims if for example the token is
// expired
// 4. Validate the token
Claims claims = Jwts.parser().setSigningKey(jwtConfig.getSecret().getBytes()).parseClaimsJws(token)
.getBody();
String username = claims.getSubject();
if (username != null) {
@SuppressWarnings("unchecked")
List<String> authorities = (List<String>) claims.get("authorities");
// 5. Create auth object
// UsernamePasswordAuthenticationToken: A built-in object, used by spring to
// represent the current authenticated / being authenticated user.
// It needs a list of authorities, which has type of GrantedAuthority interface,
// where SimpleGrantedAuthority is an implementation of that interface
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null,
authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
// 6. Authenticate the user
// Now, user is authenticated
SecurityContextHolder.getContext().setAuthentication(auth);
}
} catch (Exception e) {
// In case of failure. Make sure it's clear; so guarantee user won't be
// authenticated
SecurityContextHolder.clearContext();
}
// go to the next filter in the filter chain
chain.doFilter(request, response);
}
}
我也试过传递正确的持票人令牌,但没有改变。
请查看日志
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/userservice/hostdata/gethostnames'; against '/auth/**'
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/userservice/hostdata/gethostnames'; against '/api/**'
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', GET]
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/userservice/hostdata/gethostnames' doesn't match 'GET /logout
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', POST]
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/userservice/hostdata/gethostnames'; against '/logout'
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', PUT]
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/userservice/hostdata/gethostnames' doesn't match 'PUT /logout
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', DELETE]
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/userservice/hostdata/gethostnames' doesn't match 'DELETE /logout
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
2019-02-01 22:28:12.356 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 5 of 11 in additional filter chain; firing Filter: 'JwtTokenAuthenticationFilter'
am in JwtTokenAuthenticationFilter
2019-02-01 22:28:12.467 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2019-02-01 22:28:12.467 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2019-02-01 22:28:12.467 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2019-02-01 22:28:12.467 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.a.AnonymousAuthenticationFilter : SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@7da74079: Principal: demo; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_user, admin'
2019-02-01 22:28:12.467 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
2019-02-01 22:28:12.467 DEBUG 8456 --- [nio-8084-exec-3] s.CompositeSessionAuthenticationStrategy : Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy@63d25458
2019-02-01 22:28:12.470 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2019-02-01 22:28:12.470 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2019-02-01 22:28:12.470 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/userservice/hostdata/gethostnames'; against '/api/**'
2019-02-01 22:28:12.470 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /api/userservice/hostdata/gethostnames; Attributes: [permitAll]
2019-02-01 22:28:12.470 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@7da74079: Principal: demo; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_user, admin
2019-02-01 22:28:12.470 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@4d26c5a9, returned: 1
2019-02-01 22:28:12.470 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
2019-02-01 22:28:12.470 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor : RunAsManager did not change Authentication object
2019-02-01 22:28:12.470 DEBUG 8456 --- [nio-8084-exec-3] o.s.security.web.FilterChainProxy : /api/userservice/hostdata/gethostnames reached end of additional filter chain; proceeding with original chain
2019-02-01 22:28:12.472 INFO 8456 --- [nio-8084-exec-3] com.cavion.filter.LoggerFilter : POST request to http://localhost:8084/api/userservice/hostdata/gethostnames
2019-02-01 22:28:12.492 DEBUG 8456 --- [nio-8084-exec-3] **o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@3c41ee75**
2019-02-01 22:28:12.495 DEBUG 8456 --- [nio-8084-exec-3] o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
2019-02-01 22:28:12.495 DEBUG 8456 --- [nio-8084-exec-3] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
任何人都可以查看它并帮助我解决路由问题?
似乎/api/**
允许所有人,这不是你想要的。毕竟,您需要JWT的令牌认证
.antMatchers("/api/**").permitAll()
.and()
.authorizeRequests()
.antMatchers("/api/**").permitAll()
.antMatchers("/auth/**").permitAll()
.and()
.antMatcher("/api/**") // if we mention the antmatcher first , it will apply only if the url starts with /api
.authorizeRequests()
.anyRequest()
.authenticated()
Spring Security分两个阶段进行
- 身份验证 - 证明您就是您所说的人!
- 授权 - 你被允许做你将要做的事情吗?也称为访问控制
我们来看一个示例安全过滤器(伪代码删除任何多余的方法)
http
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.antMatchers("/auth/**").permitAll()
.anyRequest().denyAll()
.httpBasic()
.formLogin()
.oauth2ResourceServer().jwt()
此过滤器将接受三种不同的身份验证方法
- http-basic - https://tools.ietf.org/html/rfc7617
- OAuth2令牌(使用JWT) - https://tools.ietf.org/html/rfc6749
- 表单登录(带有正文的HTTP POST)
这些配置均插入过滤器。该过滤器将自动检测凭据是否是请求的一部分,并仅在有凭据时执行身份验证。
最重要的是要理解,如果作为传入请求的一部分没有凭据,则过滤器将不执行任何操作(无访问控制),并且过滤器链将继续。
一旦Spring Security运行了所有身份验证过滤器,就可以进行授权或访问控制了。此请求是否具有调用此URL的权限?
- 端点需要身份验证
.antMatchers("/api/**").authenticated()
这告诉Spring Security,只要请求被认证,您就可以调用以/ api开头的任何内容。它不关心身份验证的来源。它可以是基本的,可以是登录形式,也可以是持票人令牌。最后一句话非常重要。如果您的过滤器链中有多种身份验证方法,则其中任何一种都可以使用。
- 端点不需要身份验证
.antMatchers("/api/**").permitAll()
如果端点是打开的,那么我们使用permitAll。在这里,我们告诉Spring Security可以使用或不使用身份验证访问这些端点。
如果我在同一条路径上放置两个匹配器,就像在问题中一样
.antMatchers("/api/**").permitAll()
.antMatchers("/api/**").authenticated()
Spring Security将按顺序运行它们,并使用第一个命中。在这种情况下,permitAll
返回true,授予访问权限。无需检查任何其他授权配置。
那该怎么办?我的理解是你希望/api/**
受到保护。而且您不关心用户拥有的组,角色或范围。只要用户通过身份验证,就可以访问/api/**
端点。在那种情况下,你想拥有
.antMatchers("/api/**").authenticated()
而且你不想使用permitAll
以上是关于应用安全筛选器后,Zuul Routing不会重定向到给定的URL的主要内容,如果未能解决你的问题,请参考以下文章
Spring Cloud(Dalston.SR5)--Zuul 网关-路由配置