preface
Shiro is an open source framework of Apache. It is a permission management framework to implement user authentication and user authorization. Spring includes Spring Security (formerly Acegi), a permissions framework that relies too closely on Spring and is not as easy to use as Shiro. Shiro does not depend on Spring, shiro can not only realize the permission management of Web applications, but also realize the permission management of C/S systems and distributed systems. Shiro is a lightweight framework, and more and more enterprise projects begin to use Shiro.
Shiro runs process learning notes
Shiro was used in the project, so take a closer look at Shiro.
I don’t know where to start, but let’s start with Shiro’s workflow.
Run the process
- First call
Subject.login(token)
Log in, and it automatically delegates toSecurity Manager
, must pass before callingSecurityUtils.setSecurityManager()
Settings; SecurityManager
Responsible for the real authentication logic; It will delegate toAuthenticator
Perform authentication;Authenticator
Is the real authenticator,Shiro API
The identity authentication entry point of the core, where you can insert your own implementation;Authenticator
Might delegate to the correspondingAuthenticationStrategy
Multi-realm authentication, defaultModularRealmAuthenticator
Will be calledAuthenticationStrategy
Multi-realm authentication;Authenticator
Will put the correspondingtoken
The incomingRealm
, fromRealm
Gets authentication information. If no exception is returned/thrown, authentication failed. You can configure multiple configurationsRealm
, will be accessed in the appropriate order and policy.
Binding thread
Start by looking at the project source code.
Look at step 1, the subject.login (token) method.
UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
Subject subject = SecurityUtils.getSubject();
subject.login(token);
Copy the code
A UsernamePasswordToken object appears, where it calls one of its constructors.
public UsernamePasswordToken(final String username, final String password, final boolean rememberMe) { this(username, password ! = null ? password.toCharArray() : null, rememberMe, null); }Copy the code
As far as I know, this is shiro’s authentication object, which is only used to store the username and password, as well as an attribute to remember me.
One of Shiro’s utility classes is then called to get a Subject object.
public static Subject getSubject() {
Subject subject = ThreadContext.getSubject();
if (subject == null) {
subject = (new Subject.Builder()).buildSubject();
ThreadContext.bind(subject);
}
return subject;
}
Copy the code
Get a Subject object using the getSubject method.
Shiro’s built-in thread class, ThreadContext, binds the Subject object to the thread through the bind method.
public static void bind(Subject subject) {
if (subject != null) {
put(SUBJECT_KEY, subject);
}
}
Copy the code
public static void put(Object key, Object value) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
if (value == null) {
remove(key);
return;
}
ensureResourcesInitialized();
resources.get().put(key, value);
if (log.isTraceEnabled()) {
String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" +
key + "] to thread " + "[" + Thread.currentThread().getName() + "]";
log.trace(msg);
}
}
Copy the code
Shiro’s keys follow a fixed format.
public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY";
Copy the code
It’s going to put in the value of KV after a non-null judgment.
You can also get the Subject object by using the getSubject method when you want it.
When the Subject object is bound, there is also a binding of the securityManager object.
The binding of the securityManager object is in a static inner class of the Subject class (which gave me a quick look).
A line of code in the getSubject method calls the buildSubject method of the inner class.
subject = (new Subject.Builder()).buildSubject();
Copy the code
PS: The Builder design mode is used here. You can go to the rookie tutorial to learn more about it.
Go in to see the source can be seen.
The parameterless constructor is called first, and the parameterless constructor is called within the parameterless constructor.
public Builder() {
this(SecurityUtils.getSecurityManager());
}
public Builder(SecurityManager securityManager) {
if (securityManager == null) {
throw new NullPointerException("SecurityManager method argument cannot be null.");
}
this.securityManager = securityManager;
this.subjectContext = newSubjectContextInstance();
if (this.subjectContext == null) {
throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +
"cannot be null.");
}
this.subjectContext.setSecurityManager(securityManager);
}
Copy the code
The securityManager object is bound here.
Of course, he also handles the empty condition of the securityManager object, in the getSecurityManager method.
public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException {
SecurityManager securityManager = ThreadContext.getSecurityManager();
if (securityManager == null) {
securityManager = SecurityUtils.securityManager;
}
if (securityManager == null) {
String msg = "No SecurityManager accessible to the calling code, either bound to the " +
ThreadContext.class.getName() + " or as a vm static singleton. This is an invalid application " +
"configuration.";
throw new UnavailableSecurityManagerException(msg);
}
return securityManager;
}
Copy the code
The real core lies in the Object securityManager.
SecurityManager
SecurityManager is an interface that inherits the Authenticator, Authorizer classes and SessionManager for Session management mentioned in the steps.
public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException;
void logout(Subject subject);
Subject createSubject(SubjectContext context);
}
Copy the code
Let’s look at the implementation.
And these classes and interfaces have sequential inheritance relationships.
Relam
Let’s take a look at another important concept, Relam.
Realm acts as a “bridge” or “connector” between Shiro and application security data. That is, when interacting with security-related data such as user accounts and performing authentication (login) and authorization (access control), Shiro looks up a lot from the Realm configured for your application.
In this sense, a Realm is essentially a security-related DAO: It encapsulates the connection details of the data source and provides related data to Shiro when needed. When configuring Shiro, you must specify at least one Realm for authentication and/or authorization. It is possible to configure multiple Realms, but at least one is required.
Shiro has built-in Realms that can connect to a large number of secure data sources (aka directories), such as LDAP, relational databases (JDBC), ini-like text configuration resources, and properties files. If the default Realm does not meet your requirements, you can also insert your own Realm implementation that represents a custom data source.
In general, custom Relam is used.
Let’s look at the implementation.
And a custom UserRelam.
Take a look at the class diagram.
Each abstract class inherits and implements different methods.
public class UserRealm extends AuthorizingRealm
Copy the code
This inherits AuthorizingRealm and requires two methods to implement it.
PrincipalCollection principals (PrincipalCollection principals) // This abstract method belongs to the AuthorizingRealm parent class AuthenticatingRealm login authentication, Protected Abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken Token) throws AuthenticationException;Copy the code
Then take a look at the authentication method. As mentioned in the previous step, the authentication uses the Authenticator, which is the fifth step.
Authenticator
The Authenticator passes the corresponding token to the Realm and gets authentication information from the Realm. If no exception is returned/thrown, authentication failed. Multiple realms can be configured here and will be accessed in the appropriate order and policy.
Let’s go back to the previous login method.
Subject.login (token) calls the Subject’s login method in the first step, finding its final implementation, the DelegatingSubject class.
There is the login method that calls the securityManager, and the final implementation is in the DefaultSecurityManager class.
Subject subject = securityManager.login(this, token);
Copy the code
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info;
try {
info = authenticate(token);
} catch (AuthenticationException ae) {
try {
onFailedLogin(token, ae, subject);
} catch (Exception e) {
if (log.isInfoEnabled()) {
log.info("onFailedLogin method threw an " +
"exception. Logging and propagating original AuthenticationException.", e);
}
}
throw ae; //propagate
}
Copy the code
After is the validation process, here we can see the fourth step, point in going to the abstract class AuthenticatingSecurityManager. And look at a careful invocation of it.
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
return this.authenticator.authenticate(token);
}
Copy the code
Real call Relam validation is not here, but in ModularRealmAuthenticator.
It’s a process from left to right.
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); }}Copy the code
Let’s look at the doSingleRealmAuthentication method here.
Single Relam validation.
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) { if (! realm.supports(token)) { String msg = "Realm [" + realm + "] does not support authentication token [" + token + "]. Please ensure that the appropriate Realm implementation is " + "configured correctly or that the realm accepts AuthenticationTokens of this type."; throw new UnsupportedTokenException(msg); } // Call your custom Relam method here to verify. AuthenticationInfo info = realm.getAuthenticationInfo(token); if (info == null) { String msg = "Realm [" + realm + "] was unable to find account data for the " + "submitted AuthenticationToken [" + token + "]."; throw new UnknownAccountException(msg); } return info; }Copy the code
Look at Dorelam’s.
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) { AuthenticationStrategy strategy = getAuthenticationStrategy(); AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token); for (Realm realm : realms) { aggregate = strategy.beforeAttempt(realm, token, aggregate); if (realm.supports(token)) { AuthenticationInfo info = null; Throwable t = null; Try {// call custom Relam methods to verify. info = realm.getAuthenticationInfo(token); } catch (Throwable throwable) { t = throwable; } aggregate = strategy.afterAttempt(realm, token, info, aggregate, t); } else { log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token); } } aggregate = strategy.afterAllAttempts(token, aggregate); return aggregate; }Copy the code
Relam’s getAuthenticationInfo method is called.
Seeing the familiar UserRelam, hi, closed loop.
But also only understood the general process, the specific role of each class is not very understanding, so the author still has a lot of places to learn, no, should say I was dish chicken, will learn to change with guy.
The last
You can leave a comment below to discuss what you don’t understand. You can also pay attention to my private letter and ask me. I will answer it after I see it. Also welcome everyone to pay attention to my public account: the future is bright, immediately gold nine silver ten job-hunting interview season, sorted out more than 1000 nearly more than 500 pages of PDF document Java interview questions in it, to help you realize your dream BAT! Articles will be updated in it, and sorted data will be placed in it. Thank you for watching, think the article is helpful to you remember to pay attention to me a thumb-up support!