I was in the completion of the use of Shiro for login authentication and Rights Management implementation. The requirement involves the use of three roles: students, teachers, administrators. Now you want to implement the three separate logins. That requires three Realm--studentrealm and Teacherrealm, and Adminrealm, respectively, to handle the validation functions of students, teachers, and administrators.
But under normal circumstances, when more than one realm is defined, whether it's a student login, a teacher login, or an administrator login, the three realms will work together. This is because, when multiple realms are configured, The authenticator we typically use is the Shiro Org.apache.shiro.authc.pam.ModularRealmAuthenticator, which determines the realm used by the DoAuthenticate () method, The source code is as follows:
Protected AuthenticationInfo doauthenticate (Authenticationtoken authenticationtoken) throws Authenticationexception {
assertrealmsconfigured ();
Collection<realm> Realms = Getrealms ();
if (realms.size () = = 1) {
return dosinglerealmauthentication (Realms.iterator (). Next (), Authenticationtoken);
} else {
return domultirealmauthentication (Realms, Authenticationtoken);
}
}
The code above means that if there is more than one realm, all the configured realms will be used. Only one time, use the current realm directly.
To implement the requirements, I will create a subclass of Org.apache.shiro.authc.pam.ModularRealmAuthenticator and rewrite the DoAuthenticate () method to allow specific realms to perform specific functions. How to differentiate it. I'll also create a subclass of Org.apache.shiro.authc.UsernamePasswordToken, where I'll add a field logintype that identifies the type of login, whether it's a student login, a teacher login, or an administrator login. The steps are as follows (my Code uses groovy):
Enum Logintype {
STUDENT ("STUDENT"), admin ("admin"), TEACHER ("TEACHER")
private String type
Private Logintype (String type) {
This.type = Type
}
@Override public
String toString () {
return This.type.toString ()
}
}
Next, create a new Org.apache.shiro.authc.UsernamePasswordToken subclass Usertoken
Import Org.apache.shiro.authc.UsernamePasswordToken
class Usertoken extends Usernamepasswordtoken {
//login type, Determine if the student is logged in, the teacher is logged on or the administrator logs on
private string Logintype public
usertoken (final string username, final string password, String logintype) {
super (Username,password)
this.logintype = Logintype
} public
string Getlogintype () {
return logintype
} public
void Setlogintype (String logintype) {
This.logintype = Logintype
}
}
Step three: New Org.apache.shiro.authc.pam.ModularRealmAuthenticator subclass Usermodularrealmauthenticator:
Import org.apache.shiro.authc.AuthenticationException Import Org.apache.shiro.authc.AuthenticationInfo Import Org.apache.shiro.authc.AuthenticationToken Import org.apache.shiro.authc.pam.ModularRealmAuthenticator Import Org.apache.shiro.realm.Realm Import Org.slf4j.Logger Import org.slf4j.LoggerFactory/** * When multiple realms are configured, The authenticator we typically use is the Shiro Org.apache.shiro.authc.pam.ModularRealmAuthenticator, which determines the realm used by the DoAuthenticate () method * *
Custom Authenticator * Note that the full class name of the realm should contain the string "Student" "Teacher" or "Admin" when it is necessary to define realms that handle the validation of students and teachers and administrators separately.
* And, they cannot contain each other, for example, the full class name of the realm that handles the student validation should not contain the string "Admin". */class Usermodularrealmauthenticator extends Modularrealmauthenticator {private static final Logger Logger = Logge Rfactory.getlogger (Usermodularrealmauthenticator.class) @Override protected AuthenticationInfo doauthenticate (Aut Henticationtoken Authenticationtoken) throws Authenticationexception {Logger.info ("Usermodularrealmau Thenticator:method doauthenticate () Execute ")
Determines whether Getrealms () returns a null assertrealmsconfigured ()//cast back to the custom Customizedtoken usertoken use Rtoken = (usertoken) authenticationtoken//Logon type String Logintype = usertoken?. Getlogintype ()//All realms collection<realm> Realms = Getrealms ()//login type corresponding to all realm C ollection<realm> Typerealms = new arraylist<> () for (realm Realm:realms) {if (realm?. GetName ()?. Contains (logintype)) Typerealms? Add (Realm)}//To determine whether it is a single realm or a multi-realm if (Typerealms?. Size () = = 1) {Logger.info ("dosinglerealmauthentication () Execute") return Dosinglerealmauthentica tion (typerealms?). Get (0), usertoken)} else{logger.info ("Domultirealmauthentication () execute") re
Turn domultirealmauthentication (Typerealms, Usertoken)}}
Fourth: Create a realm that handles student logins and teacher logins, and administrator logins:
I have directly posted the code in my project and you can do it according to your specific needs.
Adminshirorealm:
Package Com.ciyou.edu.config.shiro.admin Import Com.ciyou.edu.config.shiro.common.UserToken Import Com.ciyou.edu.entity.Admin Import Com.ciyou.edu.service.AdminService Import Com.ciyou.edu.service.PermissionService Import org.apache.shiro.authc.AuthenticationException Import Org.apache.shiro.authc.AuthenticationInfo Import Org.apache.shiro.authc.AuthenticationToken Import Org.apache.shiro.authc.SimpleAuthenticationInfo Import org.apache.shiro.authc.UnknownAccountException Import Org.apache.shiro.authz.AuthorizationException Import org.apache.shiro.authz.AuthorizationInfo Import Org.apache.shiro.authz.SimpleAuthorizationInfo Import Org.apache.shiro.realm.AuthorizingRealm Import Org.apache.shiro.subject.PrincipalCollection Import org.apache.shiro.util.ByteSource Import Org.slf4j.Logger Import Org.slf4j.LoggerFactory Import org.springframework.beans.factory.annotation.Autowired Import Org.springframework.context.annotation.Lazy class Adminshirorealm extends Authorizingrealm {privAte static final Logger Logger = Loggerfactory.getlogger (adminshirorealm.class) @Autowired @Lazy private Ad Minservice adminservice @Autowired @Lazy private permissionservice permissionservice @Override PR otected authenticationinfo Dogetauthenticationinfo (Authenticationtoken token) throws Authenticationexception {Lo Gger.info ("Start admin authentication ...") usertoken usertoken = (usertoken) token String adminname = usertoken?.
GetUserName ()//Get user name, default and login.html in AdminName correspond. Admin admin = adminservice?. Findbyadminname (AdminName) if (admin = = null) {//does not return the Simpleauthenticationinfo object corresponding to the login user name, it will be in the Logincon Troller throws unknownaccountexception exception throw new Unknownaccountexception ("user does not exist.
")}//Verify by returning a AuthenticationInfo instance that encapsulates the user information.
Simpleauthenticationinfo AuthenticationInfo = new Simpleauthenticationinfo (admin,//user information Admin?.
GetPassword (),//password GetName ()//realm name) Authenticationinfo.setcredentialssalt (ByteSource.Util.bytes (admin?. Getadminname ()))//Set Salt Logger.info ("Return to admin authentication information:" + AuthenticationInfo) return AuthenticationInfo}/ /When the page is accessed, the link is configured with the appropriate permissions or Shiro tag to execute this method otherwise it will not execute @Override protected authorizationinfo dogetauthorizationinfo (Principal
Collection principals) {logger.info ("Start admin permission authorization (auth!!!)") if (principals = = null) {throw new Authorizationexception ("PrincipalCollection method argument cannot be null
.")
} Simpleauthorizationinfo authorizationinfo = new Simpleauthorizationinfo () if (principals?. Getprimaryprincipal () instanceof Admin) {Admin admin = (admin) principals?. Getprimaryprincipal () logger.info ("Current admin:" + Admin) authorizationinfo?. Addrole ("admin")//every time from the database re-lookup, to ensure that the right to update the Admin? Setpermissionlist (Permissionservice?). FindpermissionbyadmIn (admin?. Getadminid ())) admin?. Getpermissionlist ()?. Each {current_permission, authorizationinfo?. Addstringpermission (current_permission?). Getpermission ())} logger.info ("Current admin authorization role:" +authorizationinfo?.) GetRoles () + ", Permissions:" + Authorizationinfo?.
Getstringpermissions ()) Return Authorizationinfo}}}
Teachershirorealm:
Package Com.ciyou.edu.config.shiro.teacher Import Com.ciyou.edu.config.shiro.common.UserToken Import Com.ciyou.edu.entity.Teacher Import com.ciyou.edu.service.TeacherService Import org.apache.shiro.authc.* Import Org.apache.shiro.authz.AuthorizationException Import org.apache.shiro.authz.AuthorizationInfo Import Org.apache.shiro.authz.SimpleAuthorizationInfo Import Org.apache.shiro.realm.AuthorizingRealm Import Org.apache.shiro.subject.PrincipalCollection Import org.apache.shiro.util.ByteSource Import Org.slf4j.Logger Import Org.slf4j.LoggerFactory Import org.springframework.beans.factory.annotation.Autowired Import Org.springframework.context.annotation.Lazy class Teachershirorealm extends Authorizingrealm {private static final Logger Logger = Loggerfactory.getlogger (teachershirorealm.class)// Add @lazy Annotations to service declarations injected in custom realms to resolve invalid @cacheble annotations//Resolve an issue that @cacheble not valid when using Redis cache data and cache Shiro @Autowired @La Zy Private Teacherservice Teacherservice @OverrIDE protected AuthenticationInfo Dogetauthenticationinfo (Authenticationtoken token) throws Authenticationexception {
Logger.info ("Start teacher authentication:") Usertoken usertoken = (usertoken) token String Teacherid = usertoken?. GetUserName () Teacher Teacher = Teacherservice?. Findbyteacherid (Teacherid) if (teacher = = null) {//does not return the Simpleauthenticationinfo object corresponding to the login user name, it will be in the Loginc Ontroller throws unknownaccountexception exception throw new Unknownaccountexception ("user does not exist.
")}//Verify by returning a AuthenticationInfo instance that encapsulates the user information.
Simpleauthenticationinfo AuthenticationInfo = new Simpleauthenticationinfo (teacher,//user information Teacher?. GetPassword (),//Password getName ()//realm name) Authenticationinfo.setcredentialssalt (Bytesou Rce. Util.bytes (teacher?). Getteacherid ()))//Set salt return AuthenticationInfo}//When the page is accessed, the link is configured with the appropriate permissions or Shiro tag to execute this method otherwise it will not execute @Overrid E proteCTED authorizationinfo Dogetauthorizationinfo (principalcollection principals) {logger.info ("Start teacher rights authorization") if (principals = = null) {throw new Authorizationexception ("PrincipalCollection method argument cannot be n
ull. ")} Simpleauthorizationinfo authorizationinfo = new Simpleauthorizationinfo () if (principals?. Getprimaryprincipal () instanceof Teacher) {authorizationinfo?.
Addrole ("Teacher") Return Authorizationinfo}}}
Studentshirorealm:
Package Com.ciyou.edu.config.shiro.student Import Com.ciyou.edu.config.shiro.common.UserToken Import Com.ciyou.edu.entity.Student Import com.ciyou.edu.service.StudentService Import org.apache.shiro.authc.* Import Org.apache.shiro.authz.AuthorizationException Import org.apache.shiro.authz.AuthorizationInfo Import Org.apache.shiro.authz.SimpleAuthorizationInfo Import Org.apache.shiro.realm.AuthorizingRealm Import Org.apache.shiro.subject.PrincipalCollection Import org.apache.shiro.util.ByteSource Import Org.slf4j.Logger Import Org.slf4j.LoggerFactory Import org.springframework.beans.factory.annotation.Autowired Import Org.springframework.context.annotation.Lazy class Studentshirorealm extends Authorizingrealm {private static final Logger Logger = Loggerfactory.getlogger (studentshirorealm.class)// Add @lazy Annotations to service declarations injected in custom realms to resolve invalid @cacheble annotations//Resolve an issue that @cacheble not valid when using Redis cache data and cache Shiro @Autowired @La Zy Private Studentservice Studentservice @OverrIDE protected AuthenticationInfo Dogetauthenticationinfo (Authenticationtoken token) throws Authenticationexception {
Logger.info ("Start student authentication:") Usertoken usertoken = (usertoken) token String StudentID = usertoken?. GetUserName () Student Student = Studentservice?. Findbystudentid (StudentID) if (student = = null) {//does not return the Simpleauthenticationinfo object corresponding to the login user name, it will be in the Loginc Ontroller throws unknownaccountexception exception throw new Unknownaccountexception ("user does not exist.
")}//Verify by returning a AuthenticationInfo instance that encapsulates the user information.
Simpleauthenticationinfo AuthenticationInfo = new Simpleauthenticationinfo (student,//user information Student?. GetPassword (),//Password getName ()//realm name) Authenticationinfo.setcredentialssalt (Bytesou Rce. Util.bytes (student?). Getstudentid ()))//Set salt return AuthenticationInfo}//When the page is accessed, the link is configured with the appropriate permissions or Shiro tag to execute this method otherwise it will not execute @Overrid E proteCTED authorizationinfo Dogetauthorizationinfo (principalcollection principals) {logger.info ("Start Student Rights authorization") if (principals = = null) {throw new Authorizationexception ("PrincipalCollection method argument cannot be n
ull. ")} Simpleauthorizationinfo authorizationinfo = new Simpleauthorizationinfo () if (principals?. Getprimaryprincipal () instanceof Student) {authorizationinfo?.
Addrole ("Student") Return Authorizationinfo}}}
The
Next is a multi-realm annotation configuration for Shiro.
The code in my project is posted directly here.
Above is the class I configured for Shiro, here are some of the main code:
SecurityManager is the core of the Shiro architecture, which links realms and users (referred to in the document as subject.) @Bean public SecurityManager SecurityManager () {Defaultwebsecuritymanager SecurityManager = new Defaultwebsec
Uritymanager ()//Set realm.
Securitymanager.setauthenticator (Modularrealmauthenticator ()) list<realm> Realms = new arraylist<> () Add multiple Realm Realms.add (Adminshirorealm ()) Realms.add (Teachershirorealm ()) Realms.add (Studen Tshirorealm ()) Securitymanager.setrealms (Realms)//custom cache implementation using Redis Securitymanager.setcachemanage R (CacheManager ())//custom session management using Redis Securitymanager.setsessionmanager (SessionManager ())//injection
Remember my manager; Securitymanager.setremembermemanager (Remembermemanager ()) return SecurityManager}/** * System comes with a realm pipe * */@Bean public modularrealmauthenticator Modularrealmauthenticator () {///self-rewriting Modularr
Ealmauthenticator Usermodularrealmauthenticator modularrealmauthenticator = new Usermodularrealmauthenticator () ModularRealmA
Uthenticator.setauthenticationstrategy (New Atleastonesuccessfulstrategy ()) return Modularrealmauthenticator}
@Bean public Adminshirorealm Adminshirorealm () {Adminshirorealm Adminshirorealm = new Adminshirorealm ()
Adminshirorealm.setcredentialsmatcher (Hashedcredentialsmatcher ())//Set decryption rule return Adminshirorealm} @Bean public Studentshirorealm Studentshirorealm () {Studentshirorealm Studentshirorealm = new Studentshiror Ealm () Studentshirorealm.setcredentialsmatcher (Hashedcredentialsmatcher ())//Set decryption rule return Studentshiroreal m} @Bean Public Teachershirorealm Teachershirorealm () {Teachershirorealm Teachershirorealm = new Te Achershirorealm () Teachershirorealm.setcredentialsmatcher (Hashedcredentialsmatcher ())//Set decryption rule return teach
Ershirorealm}Because our password is too dense, so, if you want to Shiro authentication user identity, we need to tell it that we use MD5 encryption, and is encrypted two times. At the same time, we also returned the salt used in our own realm through Simpleauthenticationinfo.
This allows the Shiro to decrypt the password and verify that the user name and password are correct. @Bean public Hashedcredentialsmatcher Hashedcredentialsmatcher () {Hashedcredentialsmatcher hashedcredentialsm
Atcher = new Hashedcredentialsmatcher () hashedcredentialsmatcher.sethashalgorithmname ("MD5")//hash algorithm: Here the MD5 algorithm is used;
Hashedcredentialsmatcher.sethashiterations (2)//hash number, such as hash two times, equivalent to MD5 (MD5 (""));
return hashedcredentialsmatcher; }
Next is the controller in the implementation of the login function, here I only posted in my project Admin login code:
Package com.ciyou.edu.controller.admin
Import com.ciyou.edu.config.shiro.common.LoginType
Import Com.ciyou.edu.config.shiro.common.UserToken
Import com.ciyou.edu.entity.Admin
import com.ciyou