Как настроить фильтр для обработки не всех запросов? - PullRequest
0 голосов
/ 02 марта 2020

Я использую Spring Core, Spring MVC, Spring REST, JWT.

Привет всем!

Я изучаю Spring Security и столкнулся с проблемой.

Я хотел бы сделать аутентификацию на основе JWT; У меня есть контроллер Rest с 3 простыми методами:

Первый метод должен быть доступен для всех, второй только для зарегистрированных пользователей, а третий только для администраторов. И хотя это работает, у меня есть еще одна проблема.

Проблема в том, что мой фильтр выбрасывает (не напрямую, потому что ноль возвращается token = header.substring(7)) NullPointerException каждый раз после запуска, даже когда он еще не получил ни одного запроса (могу ошибаться, надеюсь, этот код прояснит это). Можно ли настроить фильтр для обработки только выбранных запросов или, возможно, мой подход неверен?

Спасибо за помощь!

Ошибка:

java.lang.NullPointerException
    at com.sample.config.JwtFilter.getAuthenticationByToken(JwtFilter.java:50)
    at com.sample.config.JwtFilter.doFilterInternal(JwtFilter.java:36)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Unknown Source)

My JwtFilter:

public class JwtFilter extends BasicAuthenticationFilter {

    public JwtFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);

    }

    @Override   
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String header=request.getHeader("authorization");
        UsernamePasswordAuthenticationToken authResult=getAuthenticationByToken(header);

        SecurityContextHolder.getContext().setAuthentication(authResult);

        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthenticationByToken(String header) {
            String token = header.substring(7);
            Jws<Claims> claims = Jwts.parser().setSigningKey("fQx]n}YmL)WuHjL".getBytes()).parseClaimsJws(token);

            String username=claims.getBody().get("name").toString();
            String role = claims.getBody().get("role").toString();
    return new UsernamePasswordAuthenticationToken(username,null,Collections.singleton(new SimpleGrantedAuthority(role)));      
    }

}

Моя конфигурация:


@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter{

    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return new AuthenticationManager() {

            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                return null;
            }
        };
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/api/test2").authenticated()      .antMatchers("/api/test3").hasRole("ADMIN")
        .and()
        .addFilter(new JwtFilter(authenticationManager())); }
}

public class SpringSecurityWebInitializer extends AbstractSecurityWebApplicationInitializer{

}

Контроллер:


@RestController
@RequestMapping("/api")
public class TestApi {

    @GetMapping("/test1")
    public String test1()
    {
        return "test1";//for everyone
    }

    @GetMapping("/test2")
    public String test2()
    {
        return "test2";//only for logged in
    }

    @GetMapping("/test3")
    public String test3()
    {
        return "test3";//only for admins 
    }   
}

1 Ответ

1 голос
/ 02 марта 2020

Как вы настроили ваше приложение, ваш фильтр будет вызываться для каждого запроса, потому что безопасность FilterChain «применяется» к каждому запросу.

Эта строка:

http.authorizeRequests()
        .antMatchers("/api/test2").authenticated()      
        .antMatchers("/api/test3").hasRole("ADMIN")
        ...

... не препятствует выполнению запроса через FilterChain. Он просто позволяет запросу, соответствующему этим путям, проходить через фильтры без проверки подлинности. Но каждый фильтр, который вы регистрируете (например, JwtFilter), будет вызван.

Решение

Вы можете отключить весь FilterChain для определенных запросов, переопределив метод configure(WebSecurity) в WebSecurityConfigurerAdapter :

@Override
public void configure(WebSecurity web) {
    web.ignoring().mvcMatchers(...);
}

Но в вашем случае лучше изменить фильтр так, чтобы он пропускал определенные запросы в зависимости от пути входящего запроса. Вы можете сделать что-то вроде:

public class JwtFilter extends BasicAuthenticationFilter {
    private AntPathRequestMatcher filterPath;

    // the path you provide will restrict the filter to only be "applied"
    // to requests with that path
    public JwtFilter(AuthenticationManager authenticationManager, String path) {
        super(authenticationManager);
        this.filterPath = new AntPathRequestMatcher(path);
    }

    @Override   
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        // the filter will do nothing if the request doesn't match
        if(!filterPath.matches(request) {
            chain.doFilter(request, response);
        }

        // your filter logic goes here...

        chain.doFilter(request, response);
    }

    // ...
}


...