1. Define entities and relationships
That is, the user-role is a many-to-many relationship, the role-permission is a many-to-many relationship, and the user and permissions through the role of the relationship between, and in the system through the authentication of permissions, the role is only a collection of permissions, known as the display role, the permissions should correspond to resources (such as menu, URL Java method, etc.), that is, the permission string should be stored in the resource entity, but for the sake of simplification, a permission table is extracted directly, and the comprehensive Example section uses the complete table structure.
User entities include: Number (ID), User name (username), password (password), salt (salted), whether locked (locked), and whether the lock is used for blocking users, it is better to use the enum field storage, can achieve more complex user state implementation.
Role entities include:, number (ID), role Identifier (role), description (description), availability (available), where role identifiers are used to perform implicit role judgments in the program, and descriptions are used for future display of the foreground interface, is available to indicate whether the role is currently active.
Permission entities include: Number (ID), permission identifier (permission), description (description), availability (available), meaning and role entity similar no longer elaborated.
There are also two relationship entities: User-Role Entity (user number, role number, combined as composite primary key), role-permission entity (role number, permission number, and composite primary key).
SQL and entities refer to the corresponding entities in the source code for Sql\shiro.sql and com.github.zhangkaitao.shiro.chapter6.entity.
2. Environment Preparation
In order to facilitate the database operation, the use of "org.springframework:spring-jdbc:4.0.0.release" dependency, although the Spring4 version, but the use of spring3 and no difference. Other dependencies please refer to the source code of Pom.xml.
3. Define Service and Dao
In order to achieve the simplicity, only to achieve the necessary functions, the other can be implemented by themselves.
Permissionservice
Public interface Permissionservice {public
Permission createpermission (Permission Permission);
public void DeletePermission (Long permissionid);
}
Implement basic Create/delete permissions.
RoleService
Public interface RoleService {public
role createrole (role role);
public void DeleteRole (Long roleid);
Add roles-Permissions relationship public
void Correlationpermissions (Long Roleid, long ... permissionids);
Remove roles-permissions relationship public
void Uncorrelationpermissions (Long Roleid, long ... permissionids);/
}
Association/remove associated roles-permissions function with respect to permissionservice.
UserService
Public interface UserService {public
user createUser (user user);//Create account public
void ChangePassword (Long userId, String newpassword)//Modify password public
void Correlationroles (Long userId, long ... roleids);//Add User-Role relationship public
void Uncorrelationroles (Long userId, long ... roleids);//Remove user-Role Relationship public
user Findbyusername (String username);// Find user public
set<string> findroles (String username) based on user name;//find its role public based on user name
set<string> Findpermissions (String username); Find its permissions based on user name
}
Here you use Findbyusername, Findroles, and findpermissions to find the account number, role, and permission information for your user name. The following realm uses these methods to find relevant information.
Userserviceimpl
Public user createUser (user user) {
///Encryption Password
passwordhelper.encryptpassword (user);
return Userdao.createuser (user);
public void ChangePassword (Long userId, String newpassword) {
user User =userdao.findone (userId);
User.setpassword (newpassword);
Passwordhelper.encryptpassword (user);
Userdao.updateuser (user);
}
The Generate password operation is delegated to passwordhelper directly when the account is created and the password is changed.
Passwordhelper
public class Passwordhelper {
private randomnumbergenerator randomnumbergenerator =
New Securerandomnumbergenerator ();
Private String Algorithmname = "MD5";
Private final int hashiterations = 2;
public void Encryptpassword (user user) {
user.setsalt (randomnumbergenerator.nextbytes (). Tohex ());
String newpassword = new Simplehash (
algorithmname,
User.getpassword (),
ByteSource.Util.bytes ( User.getcredentialssalt ()),
hashiterations). Tohex ();
User.setpassword (NewPassword);
}
}
The subsequent credentialsmatcher need to be the same as the encryption algorithm here. The User.getcredentialssalt () helper method returns Username+salt.
In order to save space, for Dao/service interface and implementation, please refer to the source code COM.GITHUB.ZHANGKAITAO.SHIRO.CHAPTER6. Also refer to the service tier test case com.github.zhangkaitao.shiro.chapter6.service.ServiceTest.
4. Define Realm
Retrylimithashedcredentialsmatcher
As in the fifth chapter, the code is not listed here, please refer to the source com.github.zhangkaitao.shiro.chapter6.credentials.RetryLimitHashedCredentialsMatcher.
Userrealm
Also refer to the service tier test case com.github.zhangkaitao.shiro.chapter6.service.ServiceTest.
public class Userrealm extends Authorizingrealm {private UserService userservice = new Userserviceimpl (); Protected Authorizationinfo Dogetauthorizationinfo (PrincipalCollection principals) {String username = (String) pri
Ncipals.getprimaryprincipal ();
Simpleauthorizationinfo authorizationinfo = new Simpleauthorizationinfo ();
Authorizationinfo.setroles (Userservice.findroles (username));
Authorizationinfo.setstringpermissions (userservice.findpermissions (username));
return authorizationinfo;
} protected AuthenticationInfo Dogetauthenticationinfo (Authenticationtoken token) throws Authenticationexception {
String username = (string) token.getprincipal ();
User user = Userservice.findbyusername (username); if (user = = null) {throw new unknownaccountexception ();//No Account found} if (Boolean.TRUE.equals (USER.G Etlocked ())) {throw new lockedaccountexception ();//Account lockout}//GiveAuthenticatingrealm use Credentialsmatcher for password matching, if you feel that someone else's bad can be judged or custom implementation simpleauthenticationinfo AuthenticationInfo =
New Simpleauthenticationinfo (User.getusername (),//user name User.getpassword (),//password
ByteSource.Util.bytes (User.getcredentialssalt ()),//salt=username+salt getName ()//realm name
);
return authenticationinfo; }}
1, Userrealm parent class Authorizingrealm will get subject related information into two steps: Obtain authentication information (DOGETAUTHENTICATIONINFO) and authorization information (DOGETAUTHORIZATIONINFO) ;
2, Dogetauthenticationinfo get authentication related information: First obtain the user information according to the incoming username, and then if the user is empty, then throws no account exception found Unknownaccountexception If user finds but locks out the throw lock exception lockedaccountexception; and finally generates AuthenticationInfo information. To the indirect parent class Authenticatingrealm use Credentialsmatcher to determine if the password matches, if the mismatch throws a password error exception incorrectcredentialsexception In addition, if the password retry here too much will throw out of the retry count exception excessiveattemptsexception, when assembling simpleauthenticationinfo information, need to pass in: Identity information (user name), credentials (ciphertext password), Salt (username+salt), Credentialsmatcher uses salt to encrypt the incoming plaintext password and match the ciphertext password here.
3, Dogetauthorizationinfo get authorization information: PrincipalCollection is an identity collection, because we are now a realm, so call Getprimaryprincipal directly to get the previous user name can be The UserService interface is then called from the user name to obtain the role and permission information.
5. Test Cases
To save space, please refer to test case com.github.zhangkaitao.shiro.chapter6.realm.UserRealmTest. Includes: Login successful, user name error, password error, password exceeded retry count, have/no role, have/not permissions of the test. 6.2 Authenticationtoken
Authenticationtoken is used to collect user-submitted identities (such as user names) and credentials (such as passwords):
Public interface Authenticationtoken extends Serializable {
object Getprincipal ();//Identity
Object Getcredentials ( ); Credentials
}
Extended Interface Remembermeauthenticationtoken: Provides a "Boolean isrememberme ()" Now "Remember Me" function;
The extension interface is Hostauthenticationtoken: Provides a "String gethost ()" method to obtain the user's "host" functionality.
Shiro provides a direct-to-use Usernamepasswordtoken for implementing the username/password Token Group, In addition, it realizes Remembermeauthenticationtoken and Hostauthenticationtoken, which can realize the support of remembering me and host authentication. 6.3 AuthenticationInfo
AuthenticationInfo has two functions:
1. If realm is a authenticatingrealm subclass, it is provided to the Credentialsmatcher for Authenticatingrealm internal use for credential verification ; (if it is not inherited, it needs to authenticate itself in its own realm);
2, provide to SecurityManager to create subject (provide identity information);
Mergableauthenticationinfo is used to provide the ability to merge AuthenticationInfo at multiple realms, mainly merging principal, if other such as Credentialssalt, Will cover the front with the information behind it.
For example, Hashedcredentialsmatcher, the validation will determine whether AuthenticationInfo is a saltedauthenticationinfo subclass, to obtain salt information.
Account is the equivalent of our previous user,simpleaccount is an implementation, in Inirealm, Propertiesrealm this static creation of accounts information in the scene to use, These realms directly inherit the Simpleaccountrealm, while Simpleaccountrealm provides the relevant APIs to dynamically maintain the simpleaccount, that is, these APIs can be dynamically deleted and modified Simpleaccount Dynamic additions and deletions change the role/permissions information. And if your account is not particularly many, you can use this method, please refer to Simpleaccountrealm Javadoc.
Other conditions generally return to simpleauthenticationinfo. 6.4 PrincipalCollection
Because we can configure multiple realms at the same time in Shiro, there may be multiple identities, so it provides principalcollection to aggregate these identities:
Public interface PrincipalCollection extends Iterable, Serializable {
Object getprimaryprincipal ();//Get Primary identity
<T> T onebytype (class<t> type); Get the first
<T> collection<t> bytype (class<t> type) based on identity type;//Get a
list of aslist () based on identity type;// Convert to List
set AsSet ();//Convert to set
Collection Fromrealm (String realmname);//Get set<string> by realm name
Getrealmnames (); Get all authentication through the realm name
boolean isEmpty ();//Determine if it is empty
}
Because PrincipalCollection aggregates more, the most important thing to note here is Getprimaryprincipal, if there is only one principal then return directly, if there are multiple principal, Returns the first (because the map store is used internally, so it can be thought of as returning any one); Onebytype/bytype returns the corresponding principal based on the type of credential The Fromrealm gets the corresponding principal according to realm name (each principal is associated with a realm).