Authorization of Shiro actual combat series (6)

Authorization, also known as access control, is the process of resource access management. In other words, control who has permission to do what in the application. An example of authorization check is: is the user allowed to access this web page, edit this data, view this button, or print to this printer? These are to determine which users can access.

Elements of authorization:

Permissions in Apache Shiro represent the most fundamental element of security policy. They basically make a declaration of behavior and clearly indicate what can be done in the application. A well formed permission statement basically describes the resources and the behavior that may occur when the subject interacts with these resources.

Some examples of permission statements:

 1) open a file;

 view '/ user / list' webpage;

 (3) print documents;

 (4) delete user 'jsmith';

Most resources will support a typical crud (create, read, update, delete) operations, but any behavior that makes sense to a specific resource is OK. The basic concept is that the smallest license statements are based on resources and behavior. When viewing permissions, the most important thing may be to realize that license statements have no form of representation of behavior. They are just what they can do in an application The declaration statement of the.

Permissions represent behavior only

License statements only reflect behavior (behavior related to resource types). They do not reflect who can perform such behavior.

Define what (users) are allowed to do (permission) is an application of permissions assigned to users in some way. This is usually completed by the data model of applications, and varies greatly between different applications. For example, permissions can be grouped into a role, which can be associated with one or more user objects. Or some applications can have a group that can be assigned a role The passed association means that all users in the group implicitly obtain the permissions of the role. How users are granted permissions can vary a lot -- the application decides how to model based on the requirements of the application. We will discuss later how Shiro determines whether a subject is allowed to do something.

Permission granularity

All the above permission examples detail the behavior (open, read, delete, etc.) of a resource type (entry, file, customer, etc.). In some cases, they can even specify very fine-grained instance level behavior - for example, "delete" (behavior) a "user" with a user name of "jsmith" (resource type). In Shiro, you have the ability to define the precise granularity that these claims can achieve. We will discuss the permission granularity and the "level" of license claims in more detail in Shiro's permission document.

Role permission granularity

All the above permission examples detail the behavior (open, read, delete, etc.) of a resource type (entry, file, customer, etc.). In some cases, they can even specify very fine-grained instance level behavior - for example, "delete" (behavior) a "user" with a user name of "jsmith" (resource type). In Shiro, you have the ability to define the precise granularity that these claims can achieve. We will discuss the permission granularity and the "level" of license claims in more detail in Shiro's permission document.

A role is a named entity that usually represents a set of actions or responsibilities. These behaviors evolve into what you can or can't do in a software application. Roles are usually assigned to user accounts. Therefore, through assignment, what users can "do" can belong to various roles. There are two valid types of roles, and Shiro supports these two concepts:

(1) Implicit roles: most people use roles as an implicit construct: your application contains a set of behaviors (i.e. permissions) based on only one role name. With implicit roles, it does not say "role x is allowed to perform behaviors a, B and C" at the software level. Behaviors have been implied by a separate name.

Potentially。

Potentially Brittle Security

Although there are relatively simple and most commonly used methods, implicit roles may impose many software maintenance and management problems. For example, what if you just want to add or delete a role, or redefine the behavior of a role? You have to go back to your code and rewrite all your role checks to reflect changes in your security model. Every change like this is essential! Not to mention that this will incur operating costs (retesting, QA, closing the application, checking and upgrading the software under a new role, restarting the application, etc.). This may not be a problem for very simple applications (for example, there may be an "admin" role and "others" Role). But for more complex or configurable applications, this may be the most important problem in the whole life cycle of your application and cause a large maintenance cost for your software.

(2) Explicit role: an explicit role is essentially a named collection of actual license statements. In this form, the application (and Shiro) knows exactly what a particular role means, because it is an exact behavior known whether it can be performed or not, and does not guess or imply what a particular role can or cannot do.

The Shiro team advocates using permissions and explicit roles rather than the old implicit methods. You will have more security experience in controlling applications.

Resource based access control

Be sure to read les hazlewood's article, the new RBAC: resource-based access control, which includes the benefits of deep use permissions and explicit roles (and their positive impact on source code) rather than old implicit methods

Users

Users are essentially people associated with the application. However, as we have discussed, subject is Shiro's "user" concept. Allow users (subjects) perform certain operations in your application by associating with their roles or granting direct permissions. Your application's data model defines how subjects are allowed to do something or not. For example, in your data model, maybe you have an actual user class and you assign permissions directly to the user instance. Or, you can also You can only assign permissions to roles, and then assign roles to users. Through association, users extend "yes" permissions to their own roles. Or you can replace these things with the concept of "group". It's up to you - what you use to make your program meaningful. Your data model defines how authorization works. Shiro relies on real to transform your data model so that its details are related to a format that Shiro can understand.

We'll talk about how realms does this later.

Finally, your realm is implemented to communicate with your data sources (RDBMS, LDAP, etc.). Therefore, your realm is to tell Shiro whether there are roles or permissions. You have sufficient control over the structure and definition of your authorization model.

Authorizing subjects

There are three ways to execute authorization in Shiro:

(1) Write code - you can perform authorization checks in your Java code with structures like if and else blocks. (2) JDK annotations - you can add authorization annotations to your Java methods. 

(3) JSP / GSP tag library - you can control the output of JSP or GSP pages based on roles and permissions.

(1) Programming authorization

Perhaps the simplest and most common way to perform authorization is to programmatically interact directly with the current subject instance.

(2) Role based authorization

If you want to control access based on simple / traditional implicit role names, you can perform role checking

Role checks

If you simply want to check whether the current subject has a role, you can call the variant hasrole * method on the subject instance. For example, to determine whether a subject has a special (single) role, you can call the subject.hasrole method and respond accordingly:

There are several role oriented subject methods that can be called, depending on your needs:

Role assertions

Another way is to determine whether subjects have a role by checking Boolean values. You can simply assert that they have an expected role before the logic is executed. If the subject does not have the expected role, the authorizationexception will be thrown. If they have the expected role, the assertions will execute quietly, and the logic will continue as expected.

For example:

One advantage of using the hasrole * method is that the code can be cleaned, because you don't need to create your own authorizationexception. If the current subject doesn't meet the expected conditions (if you don't want to). There are several you can

The role oriented subject assertion method called depends on your needs:

Permission based authorization

As mentioned earlier, in the roles we outlined, often a better way to perform access control is through permission based authorization. Permission based authorization, because it is closely related to the original function of your application (and the behavior on the core resources of the application), the source code of permission based authorization will change when your function changes, rather than when the security policy changes. This means that the code will rarely be affected. Compared with similar role-based authorization code.

Permission checks

If you want to check whether a subject is allowed to do something, you can call various variants of ispermitted * methods. There are two main ways to check permissions - object-based permission instances or strings representing permissions.

Object based permission checks

One possible way to perform permission checking is to instantiate org apache. shiro. authz. An instance of the permission interface and pass it to the * ispermitted method that receives the permission instance. For example, consider the following: there is a printer in the office with the unique identifier laserjet4400n. Our software needs to check whether the current user is allowed to print documents on the printer before we allow them to press the "print" button. The authority check of the above situation can be clearly expressed as follows:

In this example, I also saw a very powerful example of instance level access control checking - the ability to restrict behavior is based on personal data instances. Object based permissions are useful if:

 (1) do you want compile time type safety 

(2)  you want to ensure that permissions are described and used correctly

(3) You want to explicitly control how the license parsing logic (called license implication logic, implies method based on permission interface) is executed.

 (4) you want to ensure that permissions are accurately reflected in application resources (for example, maybe permission classes can be generated automatically at the time of project compilation based on the domain model of the project)

There are several permission oriented subject methods you can call, depending on your needs:

String based permission checks

Object based permissions can be very useful (compile time type safety, ensuring behavior, customizing implicit logic, etc.), and they sometimes feel a little "clumsy" for applications. Another way is to use normal strings to represent permissions.

example.

For example, based on the above example of print permission, we can reformulate the same string based permission check as the previous check:

This example also shows the same instance level permission check, But the important components of permissions - printer (resource type), printing (behavior), and laserjet4400n (instance ID) - all are represented by a string. This special example shows a special colon separated format, which is defined by Shiro's default org.apache.shiro.authz.permission.wildcardpermission implementation. Most people will find a format suitable for themselves. That is, the above code block (most) is a simplification of the following code:

The format of wildcard permission token specification and construction operation is deeply involved in Shiro's permission document. In addition to the default wildcard permission format of the above string, you can create and use your own string format if you like. We will discuss how to do it in the real authorization section. String based permissions are helpful because you don't have to implement an interface, and simple strings are easy to read. The disadvantage is that you do not have type safety. If you need more complex behavior, it will go beyond the range represented by the string, and you have to implement your own permission object based on the permission interface. In practice, most Shiro end users choose a string based approach for simplicity, but ultimately your application needs will determine which is better. Like the object-based permission checking method, there are also string variants to support string based permission checking:

Permission assertions

As an alternative to checking a Boolean value to determine whether subjects are allowed to do something, you can simply assert whether they have the expected permissions before the logic is executed. If the subject is not allowed, the authorizationexception exception will be thrown. If they are allowed as expected, the assertion will execute quietly and the logic will continue as expected.

For example:

Or, for the same check, use the string permission:

One advantage of using the hasrole * method is that the code can be cleaned, because you don't need to create your own authorizationexception. If the current subject doesn't meet the expected conditions (if you don't want to). There are several permission oriented subject fault methods you can call, depending on your needs:

Annotation based authorization

In addition to the call of subject API, Shiro provides a collection of Java 5 + annotations. If you like annotation based authorization control

Configuration

Before you can use Java annotations, you need to enable AOP support in your application. Although there are many different AOP frameworks, unfortunately, there is no standard for using AOP in applications.

The requireauthentication annotation the requireauthentication annotation requires that the current subject has been verified in the current session before it can be accessed or called by the annotated class / instance / method. For example:

The requiresguest annotation

The requiresguest annotation requires that the current subject is a "guest", that is, they must be accessed or called by classes / instances / methods that have not been verified or remembered in the previous session.

For example:

This is usually equivalent to the following subject based logic

@H_ 550_ 301@

The requirespermissions annotation

The requirespermissions annotation requires that the current subject be allowed one or more permissions to execute the annotated method.

The requiresroles annotation the requiresroles annotation requires the current subject to have all specified roles. If they do not, the method will not be executed and the authorizationexception exception will be thrown. For example:

The requiresuser annotation

The requiresuser annotation requires that the current subject is an application user to be accessed or called by the annotated class / instance / method. An "application user" is defined as an application that has a known identity, or is confirmed due to authentication in the current session, or the 'memberme' service in the previous session is remembered.

Authorization sequence

Now that we know how to execute authorization based on the current subject, let's see what happens inside Shiro when authorization is called. We adopted the architecture diagram of the architecture chapter, leaving only the components related to authorization highlighted. Each number represents a step in the authorization process:

Step 1: the application or framework code calls the hasrole *, checkrole *, ispermitted *, or a variant of the checkpermission * method of any subject to pass any required permissions or role representatives.

Step 2: the instance of the subject, usually the delegatingsubject (or subclass), represents the securitymanager of the application by calling almost the same hasrole *, ispermitted *, or checkpermission * methods of the securitymanager (securitymanager implements the org.apache.shiro.authz.authorizer interface, which defines the specific authorization methods of all subjects).

Step 3: securitymanager, as a basic "umbrella" component, takes over / represents its internal org apache. shiro. authz. The authorizer instance calls the respective hasrole * or checkpermissions * methods of the authorizer. By default, the author instance is a modularrealmauthorizer instance, which supports coordinating one or more real instances during any authorization operation.

Step 4: each configured realm is checked to see if it implements the same authorizer interface. If yes, the respective hasrole *, ispermitted *, or checkpermission * methods of realm will be called.

ModularRealmAuthorizer

As mentioned earlier, the implementation of Shiro securitymanager uses a modularrealmauthorizer instance by default. Modular realmauthorizer also supports single realms and applications with multiple realms.

For any authorization operation, the modularrealmauthorizer will traverse its internal set of realms and interact with each in iterative order. The interactive functions of each realm are as follows:

1. If realm implements the authorizer interface, its various authorizer methods (hasrole *, or checkpermission *) will be called.

(1) If the method of realm causes an exception, the exception will be passed to the caller in the form of authorizationexception. This will short circuit the authorization process and any remaining realms will not be accessed by the authorization operation.

(2) If the method of the realm is a variant of hasrole * or ispermitted * that returns a Boolean value, and the return value is true, the true value will be returned immediately, and any remaining realm will be short circuited. This behavior exists as a way to improve performance. If the behavior is allowed by a realm, it means that the subject is also allowed. This is conducive to the security policy. Everything is explicitly allowed when it is prohibited by default. This is the safest type of security policy.

2. If realm does not implement the authorizer interface, it will be ignored.

Real authorization order

It is important to point out that, especially for authentication, the modularrealmauthorizer will interact with the real instance in iterative order. The modularrealmauthorizer obtains access to the realm instance according to the configuration of the securitymanager. When the authorization operation is performed, it will traverse the collection. At the same time, for each realm that implements the authorizer interface, it will call its own authorizer methods (such as hasrole *, ispermitted *, or checkpermission *).

Configuring a global permissionresolver

When performing string based permission checking, most Shiro's default realm implementations first convert the string into an actual permission instance before executing permission implication logic. This is because permission is evaluated based on the implication logic, Not a direct equality check (see the permission document for more information on the comparison between implication and equality). The logical comparison of implication can be better reflected in the code through string comparison. Therefore, most realms need to be converted or parse the submitted permission string into a corresponding instance representing permission. In order to help this conversion, Shiro supports the concept of permissionresolver Most Shiro realm implementations use a permissionresolver to support the implementation of their authorization interface methods based on string permissions: when one of the methods is called on realm, it will use permissionresolver to convert the string into a permission instance and perform inspection in this way. The default implementation of all Shiro realms is the internal wildcardpermissionresolver, which adopts Shiro's wildcardpermission string format. If you want to create your own permissionresolver implementation, perhaps to support your own permission String Syntax, and you want all configured real instances to support this syntax, you can set your permissionresolver to global, so that all realms can use one configuration.

Configuring a global rolepermissionresolver

Conceptually similar to permissionresolver, rolepermissionresolver can represent the required permission instance and perform permission check through a realm. However, the key difference from a rolepermissionresolver is that the input string is a role name, not a permission string. Rolepermissionresolver can be used inside realm when it is necessary to convert a role name into a group of specific permission instances. This is a particularly useful feature to support old or inflexible data sources that may not have the concept of permissions. For example, Many LDAP directories store role names (or group names), but do not support associating role names to specific permissions because they do not have "permissions" The concept of. A Shiro based application can use the role name stored in LDAP and implement a rolepermissionresolver to convert the LDAP name to a set of explicit permissions to perform preferred explicit access control. Permission associations will be stored in another data warehouse, possibly a local database. Because this concept of converting role names to permissions is very application specific, Shiro's default implementation of real does not use them. However, if you want to create your own rolepermissionresolver and have multiple implementations of the realms you want to configure, you can set your rolepermissionresolver to global, so that all realms can use one configuration.

Configuring a global rolepermissionresolver

Conceptually similar to permissionresolver, rolepermissionresolver can represent the required permission instance and perform permission check through a realm. However, the key difference from a rolepermissionresolver is that the input string is a role name, not a permission string. Rolepermissionresolver can be used inside realm when it is necessary to convert a role name into a group of specific permission instances. This is a particularly useful feature to support old or inflexible data sources that may not have the concept of permissions. For example, Many LDAP directories store role names (or group names), but do not support associating role names to specific permissions because they do not have "permissions" The concept of. A Shiro based application can use the role name stored in LDAP and implement a rolepermissionresolver to convert the LDAP name to a set of explicit permissions to perform preferred explicit access control. Permission associations will be stored in another data warehouse, possibly a local database. Because this concept of converting role names to permissions is very application specific, Shiro's default implementation of real does not use them. However, if you want to create your own rolepermissionresolver and have multiple implementations of the realms you want to configure, you can set your rolepermissionresolver to global, so that all realms can use one configuration.

Rolepermissionresolver aware if you want to configure a global rolepermissionresolver, each realm used to receive the configured rolepermissionresolver must implement the rolepermissionresolver aware interface. This ensures that the configured global rolepermissionrosolver instance can be forwarded by each realm that supports the configuration.

If you don't want to use the global rolepermissionresolver or you don't want to be bothered by the rolepermissionresolveaware interface, you can explicitly configure a realm with an instance of rolepermissionresolver at any time (assuming that there is a method compatible with JavaBean's setrolepermissionresolver):

Rolepermissionresolver aware if you want to configure a global rolepermissionresolver, each realm used to receive the configured rolepermissionresolver must implement the rolepermissionresolver aware interface. This ensures that the configured global rolepermissionrosolver instance can be forwarded by each realm that supports the configuration.

If you don't want to use the global rolepermissionresolver or you don't want to be bothered by the rolepermissionresolveaware interface, you can explicitly configure a realm with an instance of rolepermissionresolver at any time (assuming that there is a method compatible with JavaBean's setrolepermissionresolver):

If you don't want to use the global rolepermissionresolver or you don't want to be bothered by the rolepermissionresolveaware interface, you can explicitly configure a realm with an instance of rolepermissionresolver at any time (assuming that there is a method compatible with JavaBean's setrolepermissionresolver):

Custom authorizer

If your application uses multiple realms to perform authorization, and the modular realmauthorizer is based on simple iteration by default, and the short-circuit authorization behavior does not meet your requirements, you are likely to want to create a custom authorizer and configure the corresponding securitymanager.

Understanding Permissions in Apache Shiro

Shiro defines a permission as a statement that specifies a specific act or activity. This is an original function statement in the application, that's all. Permissions are the lowest level constructs in a security policy, and they clearly define what an application can only do. They never describe "who" can perform these actions.

Some examples of permissions:

(1) Open file

(2) Browse '/ user / list' page

(3) Print document

(4) Delete 'jsmith' user

Specifying "who" (users) is allowed to do "what" (permissions) is to some extent a customary practice of assigning permissions. This is always done through the application data model and varies greatly from application to application.

For example, permissions can be combined into a role that can be associated with one or more user objects. Or some applications can have a group of users, and the group can be assigned a role. Through the passed Association, it means that all users in the group implicitly obtain the permission of the role. How users are granted permissions can vary a lot -- applications decide how to model them based on application requirements.

Wild card permissions the above permission examples, "open file", "browse '/ user / list' page" are all valid permission statements. However, it is computationally difficult to interpret these as natural language strings and determine whether the user is allowed to perform the behavior. Therefore, in order to use permission statements that are easy to handle and still readable, Shiro provides a powerful and intuitive syntax, which we call wildcard permission.

Simple Usage

Suppose you want to protect access to your company's printers so that some people can print to a specific printer, while others can query what work is currently in the queue.

An extremely simple method is to grant the user "queryprinter" permission. Then you can check whether the user has queryprinter permission by calling

A simple permission string may work well in a simple application, but it requires you to have permissions such as "printprinter", "queryprinter" and "manageprinter". You can also grant the user "*" permission by using wildcards (giving this permission to construct its name), which means that they have all permissions in the whole application. However, using this method can not say that the user has "all printer permissions". For this reason, wildcard permissions supports multi-level permission management.

Multiple Parts

Wildcard permissions support the concept of multiple levels or parts. For example, you can adjust the previous simple example by granting user permissions.

The colon in this example is a special character that separates the next part of the permission string. In this example, the first part is the domain in which the permission is operated (printer), and the second part is the operation executed (query). Other examples above will be changed to:

There is no limit to the number of components that can be used, so it depends on your imagination, based on the methods you may use in your application.

Multiple Vaules

Each component can protect multiple values. Therefore, in addition to granting users "printer: print" and "printer: query" permissions, you can simply grant them a:

It can give users the ability to print and query printers. Since they are granted these two operations, you can judge whether the user has the ability to query the printer by calling the following statement:

The statement will return true

Multiple Vaules

Each component can protect multiple values. Therefore, in addition to granting users "printer: print" and "printer: query" permissions, you can simply grant them a:

It can give users the ability to print and query printers. Since they are granted these two operations, you can judge whether the user has the ability to query the printer by calling the following statement:

All Values

What if you want to grant all values to a user in a particular widget? This will be more convenient than listing each value manually. Similarly, I can do this based on wildcards. If there are three possible operations (query, print and manage) in the printer domain, you can do the following:

Then, any permission check on "printer: XXX" will return true. Wildcards used in this way have a better scale than explicitly listing operations. If you soon add a new operation to the application, you don't need to update the permissions of the part using wildcards. Finally, it is also possible to use wildcard token in any part of a wildcard permission string. For example, if you want to grant "view" permission to a user in all fields (not just printers), you can do this:

Instance-Level Access Control

Another common use of wildcard permissions is to shape instance level access control lists. In this case, you use three components - the first is the domain, the second is the operation, and the third is the instance that is implemented.

So take the following example:

The first defines the behavior of querying a printer with ID lp7200. The second permission defines the behavior of printing to the printer with ID epsoncolor. If you grant these permissions to users, they can perform specific behaviors on specific instances. Then you can do a check in the code:

This is a very effective way to reflect permissions. But similarly, defining multiple instance IDs for all printers can be well extended, especially when new printers are added to the system. You can use wildcards instead:

This is extended because it covers any new printer at the same time. You can even run and access all operations on all printers:

Or all operations on one printer:

Or even specific operations:

The "*" wildcard "," subassembly separator can be used for any part of the permission.

The last thing to note about missing parts is permission allocation: missing parts mean that users can access all matching values. In other words,

Checking Permissions

Although it is convenient and extensible to use wildcards for permission allocation ("printer: Print: *" =Print to any printer), but permission checking at run time should always be based on most specific permission strings. For example, if the user has a user interface and they want to print a document to the lp7200 printer, you should check whether the user is allowed to do so by executing this code.

Why?

Because the second example shows that "for the execution of the following code block, you must be able to print to any printer". But remember that "Pinter: print" is equivalent to "printer: Print: *"! Therefore, this is an incorrect check. What if the current user does not have the ability to print to any printer, but only to lp7200 and epsoncolor? Then the second example above will never allow them to print to the lp7200 printer, even if they have been given the corresponding ability! Therefore, the rule of thumb is to use permission strings whenever possible when performing permission checks. Of course, the second block above may be an effective check elsewhere in the application. If you really want to execute the code block, if the user is allowed to print to any printer (doubtful, but possible). Your application will decide what meaning to check, but generally, the more specific, the better.

Implication,not Equality

Why should runtime permission checking be as specific as possible, but permission allocation can be more common? This is because permission checking is judged by implicit logic -- not by equality checking. That is, if a user is assigned the user: * permission, it means that the user can perform the user: View operation. "User: *" string is obviously not equal to "user: View", but the former includes the latter. "User: *" describes a superset of functions defined by "user: View". In order to support implication rules, all permissions are translated to the implementation org apache. shiro. authz. In the object instance of the permission interface. This is so that implication logic can be executed at run time, and implication logic is usually more complex than a simple string equality check. All the wildcard behaviors described in this document are actually created by org apache. shiro. authz. permission. Implemented by the wildcardpermission class. Here are more wildcard permission strings accessed through logic:

Performance Considerations

Permission checking is much more complex than a simple equality comparison, so the implication logic of the runtime must execute each assigned permission. When using a permission string like the one shown above, you are implicitly using Shiro's default wildcard permission, which can perform the necessary implicit logic. Shiro's default behavior for realm implementation is, For each permission verification (for example, calling subject.ispermitted), all the permissions assigned to the user (in their groups, roles, or directly assigned to them) need to be checked separately for the implication logic. Shiro returns "short circuited" immediately through the first successful check The process to improve performance, but it is not a silver bullet. This is usually extremely fast. When users, roles and permissions are cached in memory and a suitable CacheManager is used, it is in the realm implementation not supported by Shiro. As long as you know to use this default behavior, the time to perform the check must increase when the permissions assigned to users or their roles or groups increase. If a realm implementer has a more efficient way to check permissions and execute implication logic, especially if it is based on the application data model, they should implement it as part of the realm ispermitted * method implementation. The support of the default realm / wildcard permission covers 80-90% of most use cases, but it may not be the best solution for applications with a large number of permissions to store or check at run time.

Shiro's authorization is summarized in a simple sentence. The corresponding role of the user has access to those resources.

Shiro is commonly used for Java EE project development. Many open source projects also use it, such as gun, jeeste, jeecg, etc. In a word, spring security is not widely used by Shiro.

Therefore, after learning from Shiro, you will no longer be afraid of Java permissions. In a word, I'm not afraid of any permission management mechanism since Shiro.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>