forked from apache/shiro-site
-
Notifications
You must be signed in to change notification settings - Fork 0
/
realm.md.vtl
241 lines (159 loc) · 16.5 KB
/
realm.md.vtl
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# <a name="Realm-ApacheShiroRealms"></a>Apache Shiro Realms
* [Realm Configuration](#Realm-RealmConfiguration)
* [Explicit Assignment](#Realm-ExplicitAssignment)
* [Implicit Assignment](#Realm-ImplicitAssignment)
* [Realm Authentication](#Realm-RealmAuthentication)
* [Supporting `AuthenticationTokens`](#Realm-Supporting%7B%7BAuthenticationTokens%7D%7D)
* [Handling supported `AuthenticationTokens`](#Realm-Handlingsupported%7B%7BAuthenticationTokens%7D%7D)
* [Credentials Matching](#Realm-CredentialsMatching)
* [Simple Equality Check](#Realm-SimpleEqualityCheck)
* [Hashing Credentials](#Realm-HashingCredentials)
* [Hashing and Corresponding Matchers](#Realm-HashingandCorrespondingMatchers)
* [`SaltedAuthenticationInfo`](#Realm-%7B%7BSaltedAuthenticationInfo%7D%7D)
* [Disabling Authentication](#Realm-DisablingAuthentication)
* [Realm Authorization](#Realm-RealmAuthorization)
A `Realm` is a component that can access application-specific security data such as users, roles, and permissions. The `Realm` translates this application-specific data into a format that Shiro understands so Shiro can in turn provide a single easy-to-understand [Subject](subject.html "Subject") programming API no matter how many data sources exist or how application-specific your data might be.
Realms usually have a 1-to-1 correlation with a data source such as a relational database, LDAP directory, file system, or other similar resource. As such, implementations of the `Realm` interface use data source-specific APIs to discover authorization data (roles, permissions, etc), such as JDBC, File IO, Hibernate or JPA, or any other Data Access API.
#tip('Tip', 'A Realm is essentially a security-specific <a class="external-link" href="http://en.wikipedia.org/wiki/Data_Access_Object" rel="nofollow">DAO</a>')
Because most of these data sources usually store both authentication data (credentials such as passwords) as well as authorization data (such as roles or permissions), every Shiro `Realm` can perform _both_ authentication and authorization operations.
<a name="Realm-RealmConfiguration"></a>
Realm Configuration
-------------------
If using Shiro's INI configuration, you define and reference `Realms` like any other object in the `[main]` section, but they are configured on the `securityManager` in one of two ways: explicitly or implicitly.
<a name="Realm-ExplicitAssignment"></a>
#[[###Explicit Assignment]]#
Based on knowledge of INI configuration thus far, this is an obvious configuration approach. After defining one or more Realms, you set them as a collection property on the `securityManager` object.
For example:
``` ini
fooRealm = com.company.foo.Realm
barRealm = com.company.another.Realm
bazRealm = com.company.baz.Realm
securityManager.realms = $fooRealm, $barRealm, $bazRealm
```
Explicit assignment is deterministic - you control exactly which realms are used as well as _the order_ that they will be used for authentication and authorization. Realm ordering effects are described in detail in the Authentication chapter's [Authentication Sequence](authentication.html#[[#]]#Authentication-sequence) section.
<a name="Realm-ImplicitAssignment"></a>
#[[###Implicit Assignment]]#
#danger('Not Preferred', 'Implicit assignment can cause unexpected behavior if you change the order in which realms are defined. It is recommended that you avoid this approach and use Explicit Assignment, which has deterministic behavior. It is likely Implicit Assignment will be deprecated/removed from a future Shiro release.')
If for some reason you don't want to explicitly configure the `securityManager.realms` property, you can allow Shiro to detect all configured realms and assign them to the `securityManager` directly.
Using this approach, realms are assigned to the `securityManager` instance in the _order that they are defined_.
That is, for the following `shiro.ini` example:
``` ini
blahRealm = com.company.blah.Realm
fooRealm = com.company.foo.Realm
barRealm = com.company.another.Realm
# no securityManager.realms assignment here
```
basically has the same effect as if the following line were appended:
``` java
securityManager.realms = $blahRealm, $fooRealm, $barRealm
```
However, realize that with implicit assignment, just the order that the realms are defined directly affects how they are consulted during authentication and authorization attempts. If you change their definition order, you will change how the master `Authenticator`'s [Authentication Sequence](authentication.html#[[#]]#Authentication-sequence) functions.
For this reason, and to ensure deterministic behavior, we recommend using Explicit Assignment instead of Implicit Assignment.
<a name="Realm-authentication"></a>
<a name="Realm-RealmAuthentication"></a>
Realm Authentication
--------------------
Once you understand Shiro's master [Authentication workflow](authentication.html#[[#]]#Authentication-sequence), it is important to know exactly what happens when the `Authenticator` interacts with a `Realm` during an authentication attempt.
<a name="Realm-Supporting%7B%7BAuthenticationTokens%7D%7D"></a>
#[[###Supporting `AuthenticationTokens`]]#
As mentioned in the [authentication sequence](authentication.html#[[#]]#Authentication-sequence), just before a `Realm` is consulted to perform an authentication attempt, its [`supports`](static/current/apidocs/org/apache/shiro/realm/Realm.html#[[#]]#supports-org.apache.shiro.authc.AuthenticationToken-) method is called. If the return value is `true`, only then will its `getAuthenticationInfo(token)` method be invoked.
Typically a realm will check the type (interface or class) of the submitted token to see if it can process it. For example, a Realm that processes biometric data may not understand `UsernamePasswordTokens` at all, in which case it would return `false` from the `supports` method.
<a name="Realm-Handlingsupported%7B%7BAuthenticationTokens%7D%7D"></a>
#[[###Handling supported `AuthenticationTokens`]]#
If a `Realm` `supports` a submitted `AuthenticationToken`, the `Authenticator` will call the Realm's [getAuthenticationInfo(token)](static/current/apidocs/org/apache/shiro/realm/Realm.html#[[#]]#getAuthenticationInfo-org.apache.shiro.authc.AuthenticationToken-) method. This effectively represents an authentication attempt with the `Realm's` backing data source. The method, in order:
1. Inspects the `token` for the identifying principal (account identifying information)
2. Based on the `principal`, looks up corresponding account data in the data source
3. Ensures that the token's supplied `credentials` matches those stored in the data store
4. If the credentials match, an [AuthenticationInfo](static/current/apidocs/org/apache/shiro/authc/AuthenticationInfo.html) instance is returned that encapsulates the account data in a format Shiro understands
5. If the credentials DO NOT match, an [AuthenticationException](static/current/apidocs/org/apache/shiro/authc/AuthenticationException.html) is thrown
This is the highest-level workflow for all Realm `getAuthenticationInfo` implementations. Realms are free to do whatever they want during this method, such as record the attempt in an audit log, update data records, or anything else that makes sense for the authentication attempt for that data store.
The only thing required is that, if the credentials match for the given principal(s), that a non-null `AuthenticationInfo` instance is returned that represents Subject account information from that data source.
#info('Save Time', 'Implementing `<a class="external-link" href="static/current/apidocs/org/apache/shiro/realm/Realm.html">Realm</a>` interface directly might be time consuming and error prone. Most people choose to subclass the <a class="external-link" href="static/current/apidocs/org/apache/shiro/realm/AuthorizingRealm.html">AuthorizingRealm</a> abstract class instead of starting from scratch. This class implements common authentication and authorization workflow to save you time and effort.')
<a name="Realm-CredentialsMatching"></a>
#[[###Credentials Matching]]#
In the above realm authentication workflow, a Realm has to verify that the [Subject](subject.html "Subject")'s submitted credentials (e.g. password) must match the credentials stored in the data store. If they match, authentication is considered successful, and the system has verified the end-user's identity.
#warning('Realm Credentials Matching', 'It is each Realm''s responsibility to match submitted credentials with those stored in the Realm''s backing data store, and not the `Authenticator''s` responsibility. Each `Realm` has intimate knowledge of credentials format and storage and can perform detailed credentials matching, whereas the `Authenticator` is a generic workflow component.')
The credentials matching process is nearly identical in all applications and usually only differs by the data compared. To ensure this process is pluggable and customizable if necessary, the [AuthenticatingRealm](static/current/apidocs/org/apache/shiro/realm/AuthenticatingRealm.html) and its subclasses support the concept of a [CredentialsMatcher](static/current/apidocs/org/apache/shiro/authc/credential/CredentialsMatcher.html) to perform the credentials comparison.
After discovering account data, it and the submitted `AuthenticationToken` are presented to a `CredentialsMatcher` to see if what was submitted matches what is stored in the data store.
Shiro has some `CredentialsMatcher` implementations to get you started out of the box, such as the [SimpleCredentialsMatcher](static/current/apidocs/org/apache/shiro/authc/credential/SimpleCredentialsMatcher.html) and [HashedCredentialsMatcher](static/current/apidocs/org/apache/shiro/authc/credential/HashedCredentialsMatcher.html) implementations, but if you wanted to configure a custom implementation for custom matching logic, you could do so directly:
``` java
Realm myRealm = new com.company.shiro.realm.MyRealm();
CredentialsMatcher customMatcher = new com.company.shiro.realm.CustomCredentialsMatcher();
myRealm.setCredentialsMatcher(customMatcher);
```
Or, if using Shiro's INI [configuration](configuration.html "Configuration"):
``` ini
[main]
...
customMatcher = com.company.shiro.realm.CustomCredentialsMatcher
myRealm = com.company.shiro.realm.MyRealm
myRealm.credentialsMatcher = $customMatcher
...
```
<a name="Realm-SimpleEqualityCheck"></a>
#[[####Simple Equality Check]]#
All of Shiro's out-of-the-box `Realm` implementations default to using a [SimpleCredentialsMatcher](static/current/apidocs/org/apache/shiro/authc/credential/SimpleCredentialsMatcher.html). The `SimpleCredentialsMatcher` performs a plain direct equality check of the stored account credentials with what was submitted in the `AuthenticationToken`.
For example, if a [UsernamePasswordToken](static/current/apidocs/org/apache/shiro/authc/UsernamePasswordToken.html) was submitted, the `SimpleCredentialsMatcher` verifies that the password submitted is exactly equal to the password stored in the database.
The `SimpleCredentialsMatcher` performs direct equality comparisons for more than just Strings though. It can work with most common byte sources, such as Strings, character arrays, byte arrays, Files and InputStreams. See its JavaDoc for more.
<a name="Realm-HashingCredentials"></a>
#[[####Hashing Credentials]]#
Instead of storing credentials in their raw form and performing raw/plain comparisons, a much more secure way of storing end-user's credentials (e.g. passwords) is to one-way hash them first before storing them in the data store.
This ensures that end-users' credentials are never stored in their raw form and that no one can know the original/raw value. This is a much more secure mechanism than plain-text or raw comparisons, and all security-conscious applications should favor this approach over non-hashed storage.
To support these preferred cryptographic hashing strategies, Shiro provides [HashedCredentialsMatcher](static/current/apidocs/org/apache/shiro/authc/credential/HashedCredentialsMatcher.html) implementations to be configured on realms instead of the aforementioned `SimpleCredentialsMatcher`.
Hashing credentials and the benefits of salting and multiple hash iterations are outside the scope of this `Realm` documentation, but definitely read the [HashedCredentialsMatcher JavaDoc](static/current/apidocs/org/apache/shiro/authc/credential/HashedCredentialsMatcher.html) which covers these principles in detail.
<a name="Realm-HashingandCorrespondingMatchers"></a>
#[[#####Hashing and Corresponding Matchers]]#
So how do you configure a Shiro-enabled application to do this easily?
Shiro provides multiple `HashedCredentialsMatcher` subclass implementations. You must configure the specific implementation on your realm to match the hashing algorithm you use to hash your users' credentials.
For example, let's say your application uses username/password pairs for authentication. And due to the benefits of hashing credentials described above, let's say you want to one-way hash a user's password using the [SHA-256](https://en.wikipedia.org/wiki/SHA_hash_functions) algorithm when you create a user account. You would hash the user's entered plain-text password and save that value:
``` java
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
...
//We'll use a Random Number Generator to generate salts. This
//is much more secure than using a username as a salt or not
//having a salt at all. Shiro makes this easy.
//
//Note that a normal app would reference an attribute rather
//than create a new RNG every time:
RandomNumberGenerator rng = new SecureRandomNumberGenerator();
Object salt = rng.nextBytes();
//Now hash the plain-text password with the random salt and multiple
//iterations and then Base64-encode the value (requires less space than Hex):
String hashedPasswordBase64 = new Sha256Hash(plainTextPassword, salt, 1024).toBase64();
User user = new User(username, hashedPasswordBase64);
//save the salt with the new account. The HashedCredentialsMatcher
//will need it later when handling login attempts:
user.setPasswordSalt(salt);
userDAO.create(user);
```
Since you're `SHA-256` hashing your user's passwords, you need to tell Shiro to use the appropriate `HashedCredentialsMatcher` to match your hashing preferences. In this example, we create a random salt and perform 1024 hash iterations for strong security (see the `HashedCredentialsMatcher` JavaDoc for why). Here is the Shiro INI configuration to make this work:
``` ini
[main]
...
credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
# base64 encoding, not hex in this example:
credentialsMatcher.storedCredentialsHexEncoded = false
credentialsMatcher.hashIterations = 1024
# This next property is only needed in Shiro 1.0\. Remove it in 1.1 and later:
credentialsMatcher.hashSalted = true
...
myRealm = com.company.....
myRealm.credentialsMatcher = $credentialsMatcher
...
```
<a name="Realm-%7B%7BSaltedAuthenticationInfo%7D%7D"></a>
#[[######`SaltedAuthenticationInfo`]]#
The last thing to do to ensure this works is that your `Realm` implementation must return a [SaltedAuthenticationInfo](static/current/apidocs/org/apache/shiro/authc/SaltedAuthenticationInfo.html) instance instead of a normal `AuthenticationInfo` one. The `SaltedAuthenticationInfo` interface ensures that the salt that you used when you created the user account (e.g. the `user.setPasswordSalt(salt);` call above) can be referenced by the `HashedCredentialsMatcher`.
The `HashedCredentialsMatcher` needs the salt in order to perform the same hashing technique on the submitted `AuthenticationToken` to see if the token matches what you saved in the data store. So if you use salting for user passwords (and you should!!!), ensure your `Realm` implementation represents that by returning `SaltedAuthenticationInfo` instances.
<a name="Realm-DisablingAuthentication"></a>
#[[###Disabling Authentication]]#
If for some reason, you don't want a Realm to perform authentication for a data source (maybe because you only want the Realm to perform authorization), you can disable a Realm's support for authentication entirely by always returning `false` from the Realm's `supports` method. Then your realm will never be consulted during an authentication attempt.
Of course at least one configured `Realm` needs to be able to support AuthenticationTokens if you want to authenticate Subjects.
<a name="Realm-RealmAuthorization"></a>
Realm Authorization
-------------------
TBD
#lendAHandDoc()
<input type="hidden" id="ghEditPage" value="realm.md.vtl"></input>