The OAuth2 and OIDC (OpenId Connect) are described in the previous 5 blogs, and their role is authorization and authentication. So when we get OAuth2 's access token or OIDC's ID token, how does our resource service validate that tokens have permission to perform an operation on a resource? For example, I have a api,/books, which has the following 5 operations:
Post/books |
Add a book |
Get/books/{id} |
Get a book |
Put/books/{id} |
Update a book |
Delete/books/{id} |
Delete a book |
Get/books |
Get a list of books |
Its pseudo-code is as follows:
[Route ("Books")] Public classbookscontroller:controller{[HttpGet ("")] PublicBook[] Get () {return NULL; } [HttpGet ("{bookId}")] PublicBook Get (intBOOKID) {return NULL; } [HttpPost ("")] PublicBook Post {return NULL; } [Httpput ("{bookId}")] PublicBook Put (intBookId, book book) {return NULL; } [Httpdelete ("{bookId}")] PublicBook Delete (intBOOKID) {return NULL; }}
So let's take a look at the ID Token of access TOKEN,OIDC based on OAuth2 and the traditional role-based permission control that handles the operations that control these resources.
1 OAuth2 access token scope
We all know that the final product of OAuth2 is to provide us with an access token, which contains a scope field that represents the authorization server or the resource owner Grant the scope of which resources the third-party client allows to manipulate the resource server . One thing to note here is that this authorization process can have the resources to have the participation (Authorization Code,implicit,resource owner Password Credentials Grant), It can also be without his participation (Client Credentials Grant). So, based on the books resources above, we can define a User_manager scope to control the permission control of five operations on books. Then books's scope-based permission control looks like this:
[Route ("Books")] Public classbookscontroller:controller{[HttpGet ("")] [Scope ( "Book_manager")] PublicBook[] Get () {return NULL; } [HttpGet ("{bookId}")] [Scope ( "Book_manager")] PublicBook Get (intBOOKID) {return NULL; } [HttpPost ("")] [Scope ( "Book_manager")] PublicBook Post {return NULL; } [Httpput ("{bookId}")] [Scope ( "Book_manager")] PublicBook Put (intBookId, book book) {return NULL; } [Httpdelete ("{bookId}")] [Scope ( "Book_manager")] PublicBook Delete (intBOOKID) {return NULL; }}
Note the Red section, adding a scope description for each operation. If access token has User_manager this scope (regardless of which authorization method he is OAuth2 issued, and our final code part only refers to scope), then the call to these APIs is allowed, otherwise it is considered not authorized to operate.
2 OIDC's ID token sub
Please refer to the ID token for the purpose of the ID token and what information it contains. The difference between ID token and access token is that it must contain a user's identity sub , but there is no scope because the purpose of ID token is to authenticate who the current user is, so the user must exist; Does not include licensing-related things such as what the authenticated user can do. So, for ID Token, how should our API be managed by permission? The usual practice is to use traditional color-based permission controls (Role Based access control). Its implementation details are not explained, its model is roughly: an entity (user or organization) has a set of roles, each role represents a set of permissions. Does it feel like the scope, in fact, is similar. Let's define a role librarian like this. This is deliberately separated from the scope's naming section, because its source is different, then we will eventually realize the time is independent.
1[Route ("Books")]2 Public classBookscontroller:controller3 {4[HttpGet ("")]5[Scope ("Book_manager")]6 [Role ("Librarian")]7 PublicBook[] Get () {return NULL; }8 9[HttpGet ("{bookId}")]Ten[Scope ("Book_manager")] One [Role ("Librarian")] A PublicBook Get (intBOOKID) {return NULL; } - -[HttpPost ("")] the[Scope ("Book_manager")] - [Role ("Librarian")] - PublicBook Post {return NULL; } - +[Httpput ("{bookId}")] -[Scope ("Book_manager")] + [Role ("Librarian")] A PublicBook Put (intBookId, book book) {return NULL; } at -[Httpdelete ("{bookId}")] -[Scope ("Book_manager")] - [Role ("Librarian")] - PublicBook Delete (intBOOKID) {return NULL; } -}
If a sub represents a user owned by or owned by the organization (regardless of how it is organized, ultimately we can know if the user has a role) the librarian role. Allows it to access these operations of the books.
What are the disadvantages of 3 or more of these two ways?
In fact, there are more than two, such as the built-in authorization control components in ASP. NET Core:
1 " AtLeast21 " )]2publicclass alcoholpurchasecontroller:controller3 {4public iactionresult Login () = View (); 5 6 Public Iactionresult Logout () = View (); 7 }
These are essentially the same type as the above-based scope and role-based categories. Of course we can work, but the question is, are they intuitive and flexible? Is it tedious? Is it good to meet our changing needs? There is always a feeling that the simple thing is complicated . For example, now I need to add a role, Super Administrator, then the above code needs us to make changes?
1 [HttpGet ("")]2 [Scope ("book_manager")] 3 [Role (" librarian ","Super admin")]4 publicreturnnull; }
For example, now you need to add a scope Book_reader , it can only perform the read operation, but also make a change. And even if we combine scope and role, it's messy.
4 The solution based on the minimum granularity of permissions
So what is the root cause of these problems? A: Both scope and role represent an implicit description of the information, rather than a description of a specific operational behavior. Now that we know the crux of the matter, how can we solve the problem? The principle is very simple, the use of permissions as our smallest unit, the scope and role and so on, as well as some other concepts of administrative organization Authority as a middle layer, prohibit them in the interface to verify the authorization, but only as a means of management organization Permission exist. The above code is then transformed as follows:
1[Route ("Books")]2 Public classBookscontroller:controller3 {4[HttpGet ("")]5 [Permission ("Books.read")]6 PublicBook[] Get () {return NULL; }7 8[HttpGet ("{bookId}")]9 [Permission ("Book.read")]Ten PublicBook Get (intBOOKID) {return NULL; } One A[HttpPost ("")] - [Permission ("Book.add")] - PublicBook Post {return NULL; } the -[Httpput ("{bookId}")] - [Permission ("Book.edit")] - PublicBook Put (intBookId, book book) {return NULL; } + -[Httpdelete ("{bookId}")] + [Permission ("Book.delete")] A PublicBook Delete (intBOOKID) {return NULL; } at}
We define a permission Permissionfor each operation, whether you are the scope of access token or role, will not appear here . For example, when we check that the super-administrator is not able to operate, we can pass the direct release (separating these checks from our description of the operating rights of the interface). If the scope is named Book_reader , we let Book_reader only associate Books.read and Book.read with the two Permission, and the management of this association relationship, we can be maintained through data storage, but also convenient to provide management pages to flexible configuration. And the final code is all about Permission. This approach can be called Resource Based access control or Permission Based access control.
5 Apache Shiro
These are some of my own understanding and ideas, and then I found the Apache Shiro this project, feel like to find the organization,Apache Shiro go farther, and for permission defined a set of rules . It is highly recommended to read https://shiro.apache.org/permissions.html this document. There is no such thing as a good blessing on the. Net side, and the default authorization filter is the traditional way in ASP.
However, based on the filter:iauthorizationfilter of ASP. NET core, we can replace this whole set of authorization control methods: Using code: Https://github.com/linianhui/oidc.example /tree/master/src/web.oauth2.resources;filters Code: https://github.com/linianhui/oidc.example/tree/master/src/ Aspnetcore.filters.permissions.
From this and nasty [Authorize (Roles =" librarian ", Policy ="XXX")] Say goodbye.
These are just some of the personal understanding, if there are errors, please correct me.
Reference
https://shiro.apache.org/
Highly recommended:https://shiro.apache.org/permissions.html
https://docs.microsoft.com/en-us/aspnet/core/security/authorization/
[Certification Authority] 6.Permission Based Access Control