排除 Spring Boot 中 JWT 安全检查中的 URL。

huangapple go评论75阅读模式
英文:

Exclude URLs from JWT security check in Spring Boot

问题

我有一个使用Spring Boot构建的应用程序,通过在pom.xml中添加以下依赖项来使用资源服务器进行安全性保护。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

这通常运行良好,但我需要从安全检查中排除特定的URL。我尝试通过创建自定义的WebSecurityConfigurerAdapter来实现。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class JWTSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(final WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/test");
    }
}

但是,在创建此类之后,除了访问/test之外的所有调用都会失败,因为服务器会重定向到登录页面。

我的端点看起来像这样:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/")
public class TestController {

    @GetMapping("test") // 这个端点应该被忽略
    public String test() {
        return "1.0";
    }

    @GetMapping("foo")
    public String test1() {
        return "foo";
    }
}

当请求 http://localhost:8080/test 时,我的日志输出如下:

2020-08-24 08:48:28.215 DEBUG 7473 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet        : GET "/test", parameters={}
2020-08-24 08:48:28.215 DEBUG 7473 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.test.controllers.TestController#test()
2020-08-24 08:48:28.218 DEBUG 7473 --- [nio-8080-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2020-08-24 08:48:28.218 DEBUG 7473 --- [nio-8080-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Writing ["1.0"]
2020-08-24 08:48:28.219 DEBUG 7473 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet        : Completed 200 OK

访问 http://localhost:8080/foo 端点会导致重定向到登录页面,并且没有任何日志输出。

有人能告诉我我漏掉了什么吗?我如何创建一个只能排除一些URL免受安全检查的 WebSecurityConfigurerAdapter?

请在此处找到一个示例项目:https://github.com/paulsasel/spring-boot-jwt-exclude-urls

英文:

I have a Spring Boot application which I secure with a resource server by adding these dependencies to the pom.xml.

&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
	&lt;artifactId&gt;spring-boot-starter-oauth2-resource-server&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
	&lt;artifactId&gt;spring-security-oauth2-jose&lt;/artifactId&gt;
&lt;/dependency&gt; 

This generally works well, but I need to exclude specific URLs from the security check, which I try to achieve by creating my custom WebSecurityConfigurerAdapter.

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class JWTSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	public void configure(final WebSecurity web) throws Exception {
		web.ignoring().antMatchers(&quot;/test&quot;);
	}
}

However after creating this class all calls (beside the one to /test) will fail, as the server redirects to the login page.

My endpoint look like this:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(&quot;/&quot;)
public class TestController {

	@GetMapping(&quot;test&quot;) // This endpoint should be ignored
	public String test() {
		return &quot;1.0&quot;;
	}

	@GetMapping(&quot;foo&quot;)
	public String test1() {
		return &quot;foo&quot;;
	}

}

When requesting http://localhost:8080/test my log output looks like this:

2020-08-24 08:48:28.215 DEBUG 7473 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet        : GET &quot;/test&quot;, parameters={}
2020-08-24 08:48:28.215 DEBUG 7473 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.test.controllers.TestController#test()
2020-08-24 08:48:28.218 DEBUG 7473 --- [nio-8080-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Using &#39;text/html&#39;, given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2020-08-24 08:48:28.218 DEBUG 7473 --- [nio-8080-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Writing [&quot;1.0&quot;]
2020-08-24 08:48:28.219 DEBUG 7473 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet        : Completed 200 OK

Hitting the endpoint http://localhost:8080/foo will result in the redirect to the login page and there will be not log output at all.

Can anybody tell me what I am missing? How can I create a WebSecurityConfigurerAdapter which does nothing else but excluding some URLs from the security check?

Please find a dummy project here: https://github.com/paulsasel/spring-boot-jwt-exclude-urls

答案1

得分: 1

如果在 void configure(final WebSecurity web) 中忽略了某些内容,那么请求将会完全从过滤器链中省略。请参阅 https://stackoverflow.com/questions/56388865/spring-security-configuration-httpsecurity-vs-websecurity/56389047

但这只是第一部分。还有另一个重载方法 void configure(HttpSecurity http),它会在过滤器链的后面被调用,并定义了如何保护端点。如果您不进行覆盖,默认将会是 formLogin,您可以在源代码中看到:

http
	.authorizeRequests()
		.anyRequest().authenticated()
			.and()
	.formLogin().and()
	.httpBasic();

这就是为什么它会将您重定向到登录页面。

因此,为了使用 JWT 认证,您还应该覆盖后面的那个方法,并定义您想要使用 OAuth2 资源服务器进行身份验证:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors()
            .and()
            .authorizeRequests()
                .anyRequest()
                    .authenticated()
            .and()
                .oauth2ResourceServer()
                    .jwt();
}

顺便提一下,在这里您还可以根据角色或请求类型进行检查。此外,作为另一种替代方法,您还可以在这里忽略掉 /test 端点,这对我来说是一个更清晰的选项:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors()
            .and()
            .authorizeRequests()
                .antMatchers("/test")
                    .permitAll()
                .antMatchers(HttpMethod.GET, "/foo")
                    .hasAuthority("DUMMYAUTHORITY")
                .anyRequest()
                    .authenticated()
            .and()
                .oauth2ResourceServer()
                    .jwt();
}

祝好,
Nandor

英文:

If you ignore something in void configure(final WebSecurity web) then it completely omits the request from the filter chain. See https://stackoverflow.com/questions/56388865/spring-security-configuration-httpsecurity-vs-websecurity/56389047

But that's only the first part. There is an another overload void configure(HttpSecurity http) which is called later in the chain and defines how exactly you want to secure the endpoints. If you don't override it the default will be formLogin, you can see it in the souce:

http
	.authorizeRequests()
		.anyRequest().authenticated()
			.and()
	.formLogin().and()
	.httpBasic();

That's why it redirects you to the login page.

So in order to use JWT authentication you should override the latter one too and define that you want to use an oauth2 resource server for the authentication:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors()
            .and()
            .authorizeRequests()
                .anyRequest()
                    .authenticated()
            .and()
                .oauth2ResourceServer()
                    .jwt();
}

By the way, here you can also check against roles or request types. Moreover, as an alternative approach you can also ignore the /test endpoint here which is a bit cleaner option for me:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors()
            .and()
            .authorizeRequests()
                .antMatchers(&quot;/test&quot;)
                    .permitAll()
                .antMatchers(HttpMethod.GET, &quot;/foo&quot;)
                    .hasAuthority(&quot;DUMMYAUTHORITY&quot;)
                .anyRequest()
                    .authenticated()
            .and()
                .oauth2ResourceServer()
                    .jwt();
}

Br,
Nandor

huangapple
  • 本文由 发表于 2020年8月20日 18:10:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/63502859.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定