which annotation can be used within spring security to apply method level security?
which annotation can be used within spring security to apply method level security? Ne90'dan bulabilirsiniz
Spring Method Security with PreAuthorize
This tutorial will explore two ways to configure authentication and authorization in Spring Boot using Spring Security. One method is to create a
WebSecurityConfigurerAdapter and use the fluent API to override the default settings on the
HttpSecurity object. Another is to use the
@PreAuthorize annotation on controller methods, known as method-level security or expression-based security. The latter will be the main focus of this tutorial. However, I will present some
HttpSecurity code and ideas by way of contrast.
The first authentication method is
HttpSecurity, which is global and is by default applied to all requests. Finer-grained control is possible, however, using pattern matching for endpoints, and the fluent API exposed by the
HttpSecurity is quite powerful. This is where configuration options such as OAuth 2.0, Form Login, and HTTP Basic are exposed. It is a great place to set global authentication policies.
Method-level security is implemented by placing the
@PreAuthorize annotation on controller methods (actually one of a set of annotations available, but the most commonly used). This annotation contains a Spring Expression Language (SpEL) snippet that is assessed to determine if the request should be authenticated. If access is not granted, the method is not executed and an HTTP Unauthorized is returned. In practice, using the
@PreAuthorize annotation on a controller method is very similar to using
HttpSecurity pattern matchers on a specific endpoint. There are some differences, however.
Differentiate Between Spring Security’s @PreAuthorize and HttpSecurity
The first difference is subtle, but worth mentioning.
HttpSecurity method rejects the request earlier, in a web request filter, before controller mapping has occurred. In contrast, the
@PreAuthorize assessment happens later, directly before the execution of the controller method. This means that configuration in
HttpSecurity is applied before
HttpSecurity is tied to URL endpoints while
@PreAuthorize is tied to controller methods and is actually located within the code adjacent to the controller definitions. Having all of your security in one place and defined by web endpoints has a certain neatness to it, especially in smaller projects, or for more global settings; however, as projects get larger, it may make more sense to keep the authorization policies near the code being protected, which is what the annotation-based method allows.
Another advantage that
@PreAuthorize presents over
HttpSecurity is the use of SpEL. Spring Expression Language allows you to make authorization decisions based on complex expressions that can access built-in authentication objects (such as
principal), dependency-injected method parameters, and query parameters. In this tutorial you will mostly look at two expressions:
hasRole(). The Spring docs are again a great place to dig deeper.
Before we dive into the project, I want to also mention that Spring also provides a
@PostAuthorize annotation. Not surprisingly, this is a method-level authorization annotation that is assessed after the method executes. Why would we want to do that? It allows the method to perform its own authorization checks based on whatever controller logic it likes before the annotation is assessed. The downside is that because the controller method is executed before the annotation is assessed, this could result is inefficiency, depending on the implementation.
The dependencies for this tutorial are pretty simple. You need: 1) Java 11 installed, and 2) an Okta developer account.
If you do not have Java installed, go to AdoptOpenJDK. On *nix systems, you can also use SDKMAN.
Start a Sample Project Using Spring Initializr
To get the project started, you can use the Spring Initializr. However, while it’s worth taking a gander at the page, you don’t even have to bother going there to create the project. You can use the REST API and
Open a terminal and
cd to wherever you want the project file .zip to end up. Run the command below, which will download the zipped Spring Boot project.
There isn’t much to the project to begin with except the
build.gradle file and the
DemoApplication.java class file. However, the whole project structure is there already set up for you.
build.gradle file also has the two Spring dependencies you need for this example:
Add a WebController
The sample app in its current state doesn’t do much. You need to add a controller to define some endpoints and responses.
Add a new file
This controller defines two endpoints:
/restricted. You will be adding method-level security to the
/restricted endpoint in a bit. Right now, however, no security has been configured.
Go ahead and run the application. From the root project directory, run:
Once Spring Boot has finished launching, navigate to
You’ll notice a login form appears. Whoa! Where did that come from?!
The form is automatically generated by Spring Boot. Take a look at the Spring class
WebSecurityConfigurerAdapter and the method configure(HttpSecurity http). This is where the default authentication settings are configured.
By default, all requests require authentication. Form-based authentication and HTTP Basic auth are enabled for all requests.
If you want to log in, look for a line in your console output like the following that tells you the generated password:
The username is simply “user.” This password is re-generated each time Spring Boot is run.
Authentication Versus Authorization
Before we go any further, I want to just quickly make sure two terms are clear: authentication and authorization. Authentication answers the question: who is making the request? Authorization answers the question: what are they allowed to do?
Authentication happens first, generally, unless there are specific permissions set for anonymous users (this is an implicit authentication in some ways). Authorization is based on the value of the authenticated user. The authenticated entity can be a human user or an automated service, or a service acting on behalf of a human user.
Two common authorization schemes are based on groups and roles. These two terms are often conflated and used interchangeably in less reputable places on the web, but there is an official difference. Groups define sets of users and assigns permissions to those sets of users. Users can be members of multiple groups. Roles define sets of permissions (allowed actions or resources) that can be assigned to users. In practice groups tends to be a more static, less flexible way of controller access while roles is often finely grained and can be dynamic even within a session, assigning roles for specific task and revoking them when they are no longer needed. Anybody that’s used Amazon AWS has seen this in action, often to bewildering effect.
Enable Method-level Security for Spring @PreAuthorize
What you want to do now is configure Spring Boot to allow requests on the home endpoint while restricting requests to the
You might initially think you could add
@PreAuthorize("permitAll()") to the home endpoint and this would allow all requests. However, if you try it, you’ll discover that it does nothing. That’s because the default
HttpBuilder implementation is still active, and because it’s assessed during the web request filter chain, it takes precedence. You also have to explicitly enable the method-level security annotations, otherwise, they’re ignored.
Add the following
SecurityConfig class, that will achieve both of the above goals.
@EnableGlobalMethodSecurity(prePostEnabled = true) annotation is what enables the
@PreAuthorize annotation. This can be added to any class with the
@Configuration annotation. I won’t go into any depth about them here, but you can also enable
@Secured, an older Spring Security annotation, and JSR-250 annotations.
configure(final HttpSecurity http) method overrides the default
HttpBuilder configuration. Because it’s empty, it leaves the application without authorization or authentication.
Run the app again using
./gradlew bootRun, and you’ll discover that both endpoints are now wide open.
Implement A Global Security Policy
Apps generally have to choose which global security policy they are going to structure their security around: “default to allowed” or “default to authenticated”. Is the app by-default open? Or by-default restricted? I generally default to restricted and explicitly allow any public endpoints. This scheme makes sense for the types of proprietary web applications that I work on that tend not to be public or have a relatively small public face. However, if you are working on something that is largely public with a discrete access-controlled backed, like a website with an admin panel, a more permissive scheme might make sense. You’ll look at both here.
Since the app is already wide-open, I’ll show you how to restrict a specific method first. After that, you’ll look at a couple of ways to implement more globally restrictive access policies.
WebController class, add the
@PreAuthorize annotation to the
/restricted endpoint, like this:
Run the app (
This time you will be able to navigate to the home page but going to the
/restricted endpoint gives you an (admittedly ugly) whitelabel error page.
In a production app, you’d need to override this to return a better, custom error page or otherwise handle the error (if you want to create a custom error page, take a look at the Spring docs on the subject). But you can see that the app is returning a 403 / Unauthorized, which is what you want.
Great. Now, instead, change your
WebController class to match the following:
Here you’ve used the
@PreAuthorize annotation to restrict the entire controller class to authenticated users and to explicitly allow all requests (regardless of authentication status) to the home endpoint.
I know we’ve been calling it “method-level” security, but, in fact, these
@PreAuthorize annotation can also be added to controller classes to set a default for the entire class. This is also where the
@PreAuthorize("permitAll()") comes in handy because it can override the class-level annotation.
If you run the app (
./gradlew bootRun) and try the endpoints, you’ll find that the home endpoint is open but the
/restricted endpoint is closed.
Note that if you added a second, separate web controller, all of its methods would still by-default be open and not require authentication.
A third option (my favorite on most small to medium-sized apps) is to use
HttpBuilder to require authentication for all requests by default and to explicitly allow any public endpoints. This allows you to use
@PreAuthorize to refine access control for specific methods based on users or roles or groups but makes it clear that all paths unless explicitly allowed will have some basic security applied. It also means that public paths are defined in a central place. Again this works for a certain type of project, but may not be the best structure for all projects.
To implement this, change the
WebController class to this (removing all the
And change the
SecurityConfig class to this:
What you’ve done is use
SecurityConfig class to explicitly permit all requests on the home endpoint while requiring authentication on all other endpoints. This sets a blanket minimum authentication requirement for your app. You also re-enabled form-based authentication.
Run the app using:
Navigate to the home endpoint, which is open:
And the restricted endpoint, which requires authentication:
When Spring’s login form appears, don’t forget you can use the default credentials. User is “user”, and the password is found in the console output (look for
Using generated security password:).
Advance to OAuth 2.0 Login
Form-based authentication feels pretty creaky and old these days. More and more, users expect to be able to log in using third-party sites, and due to increased security threats, there’s less motivation to manage user credentials on your own server. Okta is a software-as-service identity and access management company that provides a whole host of services. In this section, you’re going to use them to quickly implement a login form using OAuth 2.0 and OIDC (OpenID Connect).
Very, very briefly: OAuth 2.0 is an industry-standard authorization protocol and OIDC is another open standard on top of OAuth that adds an identity layer (authentication). Together they provide a structured way for programs to manage authentication and authorization and to communicate across networks and the internet. Neither OAuth nor OIDC, however, provide an implementation. They are just specs or protocols. That’s where Okta comes in. Okta has an implementation of the OAuth 2.0 and OIDC specs that allows for programs to use their services to quickly provide login, registration, and single sign-on (or social login) services. In this tutorial, you’re just going to be implementing a login function, but at the end of the tutorial, you can find links to other resources to show you how to implement social login and registration.
Before you begin, you’ll need a free Okta developer account. Install the Okta CLI and run
okta register to sign up for a new account. If you already have an account, run
okta apps create. Select the default app name, or change it as you see fit.
Choose Web and press Enter.
Select Okta Spring Boot Starter.
Accept the default Redirect URI values provided for you. That is, a Login Redirect of
http://localhost:8080/login/oauth2/code/okta and a Logout Redirect of
And that’s it on the Okta side.
Now you need to configure Spring Boot to use Okta as an OAuth 2.0 provider.
Configure Your Spring Boot App For OAuth 2.0
Making Spring Boot work with OAuth 2.0 and Okta is remarkably easy. The first step is to add the Okta Spring Boot Starter dependency. It’s totally possible to use Okta OAuth 2.0 / OIDC without using our starter; however, the starter simplifies the configuration. It also handles extracting the groups claim from the JSON Web Token and turning it into a Spring Security authority (which will look at in a bit). Take a look at the Okta Spring Boot Starter GitHub page for more info.
Update the dependencies section of your
src/main/resources directory there is an
application.properties file. Rename it to
application.yml. Add the following content:
Don’t forget to update the client-id, client-secret, and issuer values to match the values from your Okta developer account and OIDC app. Your Okta issuer should look something like
Finally, update the
Notice that all you really changed here was
Run the app:
./gradlew bootRun (you may either need to sign out of the Okta Admin Console or use an incognito window to see the login screen).
/ endpoint is still open, but when you go to the restricted endpoint:
You’ll see the Okta login screen.
Log in with your Okta credentials, and you’re authenticated!
Inspect the OAuth 2.0 User Attributes
When developing OAuth applications, I have found it helpful to be able to inspect the information Spring Boot has about the client and the authenticated user. To this end, add a new controller named
Notice that this class defines a class-wide request mapping path
/user, making the actual
The second method,
prettyPrintAttributes(), is just some sugar to format the user attributes so that they’re more readable.
Run the app:
You’ll see something like this:
I want to point out a few things. First, notice that the User Name is actually the Client ID of your OIDC application from Okta. That’s because, from the point of view of Spring Boot OAuth, the client app is the “user.” To find the actual user name and info, you have to look under the User Attributes. Keep in mind that the actual contents of these attributes vary between OAuth providers, so if you’re supporting Okta, GitHub, and Twitter, for example, so you will need to inspect these attributes for each OAuth provider to see what they’re returning.
The other important point is the User Authorities. Authorities, as used by Spring here, is a meta term for authorization information. They’re just strings. Their values are extracted from the OAuth/OIDC information. It’s up to the client app to use them properly. They may be roles, scopes, groups, etc… As far as OAuth/OIDC is concerned their usage is basically arbitrary.
To see how this works, in the next few sections you’ll add an Admin group in Okta, assign a user to that group, and restrict a method to the Admin group using the
ROLE_USER: You’ll notice that all authenticated users are assigned
ROLE_USER by Spring. This is the default, lowest-level role that is automatically assigned.
SCOPE_: Also be aware that the OAuth scopes are mapped to Spring authorities and can be used for authorization, for example in the
@PostAuthorize annotations, as you’ll see in a sec.
Activate Groups Claim on Okta
Okta doesn’t by default include the groups claim in the JSON Web Token (JWT). The JWT is what Okta uses to communicate authentication and authorization information to the client app. A deeper dive into that is available in some other blog posts linked to at the end of this one.
To configure Okta to add the groups claim, log in to the Okta Admin Console (tip:
okta login will provide you the URL you’re looking for).
Then, go to Security > API and select the
default authorization server.
Click on the Claims tab.
You are going to create two claim mappings. You’re not creating two claims, per se, but instructing Okta to add the groups claim to both the Access Token and the ID Token. You need to do this because depending on the OAuth flow, the groups claim may be extracted from either. In our case, with the OIDC flow, it’s actually the ID Token that matters, but it’s best to just add them to both so as to avoid frustration in the future. The resource server flow requires the groups claim to be in the access token.
First, add a claim mapping for token type Access Token.
Click Add Claim.
Update the following values (the other default values are fine):
Second, add a second claim mapping for token type ID Token.
Click Add Claim.
Update the following values (just the same as above except token type):
When you’re finished, your claims should look like the following.
Great! So now Okta will map all of its groups to a
groups claim on the access token and the ID token.
What happens to this groups claim on the Spring side is not necessarily obvious nor automatic. One of the benefits of the Spring Boot starter is that it automatically extracts the groups claim from the JWT and maps it to a Spring authority. Otherwise, you would need to implement your own
FYI: the name of the groups claim can be configured using the
okta.oauth2.groupsClaim property in the
application.yml file. It defaults to
Inspect The User Attributes With Groups
Run the app:
You’ll see something like this (a bunch of redundant lines omitted for clarity):
Notice a new groups user attribute (the mapped groups claim). The value,
Everyone, is the default group mapped to, well, everyone. This gets mapped to the user authority
That’s the basic idea. It’ll get a little more exciting in the next step when you add an Admin group.
Create An Admin Group in Okta
Now you want to add an Admin group on Okta. Log into your Okta org.
Go to Directory and select Groups. Click Add Group.
In the popup:
At this point, you’ve created the Admin group, but you haven’t actually assigned anybody to it!
Use Method-level Authorization To Restrict An Endpoint
WebController class, update the
/restricted endpoint method. You’re adding the following annotation to the method:
This tells Spring to check that the authenticated user has the
Admin authority, and if not, deny the request.
Run the app:
You’ll get a 403 / Unauthorized whitepage error.
Add Your User To the Admin Group
Now you need to add your Okta user to the Admin group. In the Okta Admin Console, select Directory and click Groups. Click on the Admin group, then Manage People. Add your user.
Test the Admin Group Membership
Done! Let’s see what that did. Once, again, run the Spring Boot app:
This time you’ll see the
Admin group and authority. (You may need a new browser session to see the change, or use incognito.)
And if you navigate to
http://localhost:8080/restricted, you’ll be allowed access.
Compare Spring Security Roles and Authorities
One thing that confused me initially was
Roles in Spring are authorities that have the
ROLE_ prefix (like all things in Spring, the prefix is configurable). One way to think about this is that roles are intended for large sets of permissions while authorities can be used for finer-grained control. However, that’s just a possible use. The actual implementation is up to the developer. In this tutorial, you’re actually using authorities to map to authorization groups.
The important point to remember is that if you want to user
hasRole(), you need the authority name in the claim to start with
ROLE_. For example, if you added a
ROLE_ADMIN group, and added your user to it, and the group to the OIDC app, you could use
Authorization Based On OAuth 2.0 Scopes with Spring PreAuthorize
You can also use the
@PreAuthorize annotation to limit access based on OAuth scopes. From the OAuth 2.0 scopes documentation:
If you look at the inspected
User Authorities returned from the
/user/oauthinfo endpoint, you’ll see three authorities that begin with
These correspond to the email, openid, and profile scopes. To restrict a method to a user that has a specific scope, you would use an annotation such as:
I will also point out that you can accomplish exactly the same thing (more or less exactly) using
HttpSecurity in the
SecurityConfig class doing something like this:
You can customize the scopes that the client app requests from the Okta authorization server by adding a
scopes property to the
application.yml file. For example, below, I have set the
application.yml file to request only the
openid scope, which is required for OAuth.
If you ran this request on the
/user/oauthinfo endpoint, you’d get something like this:
Notice that only one scope has been assigned to the user authorities.
Try adding a custom scope. Change
okta.oauth2.scopes property in the
application.yml file to match:
Before you run the app and try this out, you need to add the custom scope to the Okta authorization server (if you run it now you’ll get an error).
Open your Okta Admin Console and go to Security > API >
Click on the Scopes tab, then the Add Scope button.
You just added a custom scope (cunningly named
custom) to your default Okta authorization server.
One last time, run the Spring Boot app:
Success! You should see the new
SCOPE_custom in the user authorities.
Spring PreAuthorize, HttpSecurity, and Security in Spring Boot
You covered a ton of ground! You got a good look at Spring method-level security using
@PreAuthorize and saw how it relates to
HttpSecurity. You used some basic SpEL (Spring Expression Language) statements to configure authorization. You reviewed the difference between authorization and authentication. You configured Spring Boot to use Okta as an OAuth 2.0 / OIDC single sign-on provider and added a groups claim to the authentication server and the client app. You even a new Admin group and saw how to use the groups claim, mapped to a Spring authority, to restrict access. Finally, you took a look at how OAuth 2.0 scopes can be used to define authorization schemes and implement them in the app.
Next stop: rocket science!
If you’d like to check out this complete project, you can find the repo on GithHub.
If you’d like to learn more about Spring Boot, Spring Security, or secure user management, check out any of these great tutorials:
If you want to dive deeper, take a look at the Okta Spring Boot Starter GitHub Project.
If you have any questions about this post, please add a comment below. For more awesome content, follow @oktadev on Twitter, like us on Facebook, or subscribe to our YouTube channel.
Yazı kaynağı : developer.okta.com
Spring Method Security with @PreAuthorize and @Secured
Yazı kaynağı : howtodoinjava.com
Spring Security at Method Level
College Campus Training
Yazı kaynağı : www.javatpoint.com
Spring Security @PreAuthorize Annotation Example
Your email address will not be published.
Yazı kaynağı : www.appsdeveloperblog.com
Can anyone give an insight to how the @PreAuthorize annotation works?
An annotation does nothing and is nothing more than metadata. Adding an annotation to a method and without a piece of code that acts on that annotation it does nothing. So you need an annotation processor of some sort.
For most of the annotations used in Spring this means they are applied by AOP (Aspect Oriented Programming). In Spring this means, by default, that is done based on a proxy. This proxy intercepts the actual method call, applies some additional logic before/after calling the actual method.
The additional logic is provided by a MethodInterceptor or
@Aspect when using AspectJ based aspects. In the case of the
@PreAuthorize annotation this is done by the MethodSecurityInterceptor.
MethodSecurityInterceptor does is, based on the
@PreAuthorize annotation and the expression written in there (the expression is a SpEL expression) determine if the current user is authorized to access the method.
MethodSescurityInterceptor can be enabled either manually (doing the whole configuration yourself, quite tedious and error-prone) or by adding the
@EnableGlobalMethodSecurifty annotation. The latter will add an
@Configuration class and some processor to automatically configure the needed infrastructure classes.
Yazı kaynağı : stackoverflow.com
Yorumların yanıtı sitenin aşağı kısmında
Ali : bilmiyorum, keşke arkadaşlar yorumlarda yanıt versinler.