servlet-authorization-authorize-http-requests.md 8.9 KB
Newer Older
茶陵後's avatar
茶陵後 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
# Authorize HttpServletRequests with AuthorizationFilter

This section builds on [Servlet Architecture and Implementation](../architecture.html#servlet-architecture) by digging deeper into how [authorization](index.html#servlet-authorization) works within Servlet-based applications.

|   |`AuthorizationFilter` supersedes [`FilterSecurityInterceptor`](authorize-requests.html#servlet-authorization-filtersecurityinterceptor).<br/>To remain backward compatible, `FilterSecurityInterceptor` remains the default.<br/>This section discusses how `AuthorizationFilter` works and how to override the default configuration.|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

The [`AuthorizationFilter`](https://docs.spring.io/spring-security/site/docs/5.6.2/api/org/springframework/security/web/access/intercept/AuthorizationFilter.html) provides [authorization](index.html#servlet-authorization) for `HttpServletRequest`s.
It is inserted into the [FilterChainProxy](../architecture.html#servlet-filterchainproxy) as one of the [Security Filters](../architecture.html#servlet-security-filters).

You can override the default when you declare a `SecurityFilterChain`.
Instead of using [`authorizeRequests`](#servlet-authorize-requests-defaults), use `authorizeHttpRequests`, like so:

Example 1. Use authorizeHttpRequests

Java

```
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().authenticated();
        )
        // ...

    return http.build();
}
```

This improves on `authorizeRequests` in a number of ways:

1. Uses the simplified `AuthorizationManager` API instead of metadata sources, config attributes, decision managers, and voters.
   This simplifies reuse and customization.

2. Delays `Authentication` lookup.
   Instead of the authentication needing to be looked up for every request, it will only look it up in requests where an authorization decision requires authentication.

3. Bean-based configuration support.

When `authorizeHttpRequests` is used instead of `authorizeRequests`, then [`AuthorizationFilter`](https://docs.spring.io/spring-security/site/docs/5.6.2/api/org/springframework/security/web/access/intercept/AuthorizationFilter.html) is used instead of [`FilterSecurityInterceptor`](authorize-requests.html#servlet-authorization-filtersecurityinterceptor).

![authorizationfilter](https://docs.spring.io/spring-security/reference/_images/servlet/authorization/authorizationfilter.png)

Figure 1. Authorize HttpServletRequest

* ![number 1](https://docs.spring.io/spring-security/reference/_images/icons/number_1.png) First, the `AuthorizationFilter` obtains an [Authentication](../authentication/architecture.html#servlet-authentication-authentication) from the [SecurityContextHolder](../authentication/architecture.html#servlet-authentication-securitycontextholder).
  It wraps this in an `Supplier` in order to delay lookup.

* ![number 2](https://docs.spring.io/spring-security/reference/_images/icons/number_2.png) Second, `AuthorizationFilter` creates a [`FilterInvocation`](https://docs.spring.io/spring-security/site/docs/5.6.2/api/org/springframework/security/web/FilterInvocation.html) from the `HttpServletRequest`, `HttpServletResponse`, and `FilterChain`.

* ![number 3](https://docs.spring.io/spring-security/reference/_images/icons/number_3.png) Next, it passes the `Supplier<Authentication>` and `FilterInvocation` to the [`AuthorizationManager`](../architecture.html#authz-authorization-manager).

  * ![number 4](https://docs.spring.io/spring-security/reference/_images/icons/number_4.png) If authorization is denied, an `AccessDeniedException` is thrown.
    In this case the [`ExceptionTranslationFilter`](../architecture.html#servlet-exceptiontranslationfilter) handles the `AccessDeniedException`.

  * ![number 5](https://docs.spring.io/spring-security/reference/_images/icons/number_5.png) If access is granted, `AuthorizationFilter` continues with the [FilterChain](../architecture.html#servlet-filters-review) which allows the application to process normally.

We can configure Spring Security to have different rules by adding more rules in order of precedence.

Example 2. Authorize Requests

Java

```
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
	http
		// ...
		.authorizeHttpRequests(authorize -> authorize                                  (1)
			.mvcMatchers("/resources/**", "/signup", "/about").permitAll()         (2)
			.mvcMatchers("/admin/**").hasRole("ADMIN")                             (3)
			.mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")   (4)
			.anyRequest().denyAll()                                                (5)
		);

	return http.build();
}
```

|**1**|                                                  There are multiple authorization rules specified.<br/>Each rule is considered in the order they were declared.                                                  |
|-----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|             We specified multiple URL patterns that any user can access.<br/>Specifically, any user can access a request if the URL starts with "/resources/", equals "/signup", or equals "/about".             |
|**3**|Any URL that starts with "/admin/" will be restricted to users who have the role "ROLE\_ADMIN".<br/>You will notice that since we are invoking the `hasRole` method we do not need to specify the "ROLE\_" prefix.|
|**4**|Any URL that starts with "/db/" requires the user to have both "ROLE\_ADMIN" and "ROLE\_DBA".<br/>You will notice that since we are using the `hasRole` expression we do not need to specify the "ROLE\_" prefix. |
|**5**|                     Any URL that has not already been matched on is denied access.<br/>This is a good strategy if you do not want to accidentally forget to update your authorization rules.                     |

You can take a bean-based approach by constructing your own [`RequestMatcherDelegatingAuthorizationManager`](architecture.html#authz-delegate-authorization-manager) like so:

Example 3. Configure RequestMatcherDelegatingAuthorizationManager

Java

```
@Bean
SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> access)
        throws AuthenticationException {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().access(access)
        )
        // ...

    return http.build();
}

@Bean
AuthorizationManager<RequestAuthorizationContext> requestMatcherAuthorizationManager(HandlerMappingIntrospector introspector) {
    RequestMatcher permitAll =
            new AndRequestMatcher(
                    new MvcRequestMatcher(introspector, "/resources/**"),
                    new MvcRequestMatcher(introspector, "/signup"),
                    new MvcRequestMatcher(introspector, "/about"));
    RequestMatcher admin = new MvcRequestMatcher(introspector, "/admin/**");
    RequestMatcher db = new MvcRequestMatcher(introspector, "/db/**");
    RequestMatcher any = AnyRequestMatcher.INSTANCE;
    AuthorizationManager<HttpRequestServlet> manager = RequestMatcherDelegatingAuthorizationManager.builder()
            .add(permitAll, (context) -> new AuthorizationDecision(true))
            .add(admin, AuthorityAuthorizationManager.hasRole("ADMIN"))
            .add(db, AuthorityAuthorizationManager.hasRole("DBA"))
            .add(any, new AuthenticatedAuthorizationManager())
            .build();
    return (context) -> manager.check(context.getRequest());
}
```

You can also wire [your own custom authorization managers](architecture.html#authz-custom-authorization-manager) for any request matcher.

Here is an example of mapping a custom authorization manager to the `my/authorized/endpoint`:

Example 4. Custom Authorization Manager

Java

```
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .mvcMatchers("/my/authorized/endpoint").access(new CustomAuthorizationManager());
        )
        // ...

    return http.build();
}
```

Or you can provide it for all requests as seen below:

Example 5. Custom Authorization Manager for All Requests

Java

```
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest.access(new CustomAuthorizationManager());
        )
        // ...

    return http.build();
}
```

[Authorization Architecture](architecture.html)[Authorize HTTP Requests with FilterSecurityInterceptor](authorize-requests.html)