shiro登陆流程源码详解
生活随笔
收集整理的這篇文章主要介紹了
shiro登陆流程源码详解
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前言
抽取了shiro最基本的登陸流程(web登陸是基于這層開發的)。
詳解源碼
創建一個AuthenticationToken進行登錄。
SecurityUtils.getSecurityManager().login(null,authenticationToken); 復制代碼登陸主流程
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {AuthenticationInfo info;try {/*** 取獲校驗token的信息* 如果有返回就認為登陸成功* 拋出任何AuthenticationException子類錯誤 就認為登陸失敗*/info = authenticate(token);} catch (AuthenticationException ae) {throw ae;}/*** 走到這里* 證明已經登陸成功* * 下一步就是創建Subject*/Subject loggedIn = createSubject(token, info, subject);return loggedIn; } 復制代碼登陸身份校驗
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {/*** 使用登陸器去登陸* 默認使用ModularRealmAuthenticator*/return this.authenticator.authenticate(token); } 復制代碼protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {assertRealmsConfigured();Collection<Realm> realms = getRealms();if (realms.size() == 1) {return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);} else {//只演示單realm的情況 // return doMultiRealmAuthentication(realms, authenticationToken);return null;} } 復制代碼/*** 只有一個realm的情況下使用* @param realm* @param token* @return*/protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {/*** 先判斷這個realm是否可以校驗這個token* 這個方法需要我們在實現自己的realm時重寫*/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);}/*** 從我們自己的realm中獲取校驗后的登陸信息*/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; } 復制代碼創建Subject
/*** 登陸成功后創建Subject* 如果已經有subject* 則把現在的認證信息與原來的Subject綁定* 如果沒有Subject 則創建一個 并執行綁定操作* @param token* @param info* @param existing* @return*/ protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) {SubjectContext context = createSubjectContext();context.setAuthenticated(true); //增加了登陸成功的標志/*** 同時保存了登陸的token和realm認證后返回的信息*/context.setAuthenticationToken(token); context.setAuthenticationInfo(info);if (existing != null) {context.setSubject(existing);}return createSubject(context); } 復制代碼public Subject createSubject(SubjectContext subjectContext) {//復制了一遍subjectContextSubjectContext context = copy(subjectContext);//確保存在SecurityManagercontext = ensureSecurityManager(context);//解析Sessioncontext = resolveSession(context);Subject subject = doCreateSubject(context);/*** 保存subject* web情況下 會保存在session中*/save(subject);return subject; } 復制代碼protected Subject doCreateSubject(SubjectContext context) {//使用Subject工廠統一創建Subjectreturn getSubjectFactory().createSubject(context); } 復制代碼public Subject createSubject(SubjectContext context) {SecurityManager securityManager = context.resolveSecurityManager();//解析sessionSession session = context.resolveSession();/*** session是否自動創建標記* ,沒開啟 會報錯 @DelegatingSubject$getSession(boolean create)*/boolean sessionCreationEnabled = context.isSessionCreationEnabled();//realm中返回的用戶憑證信息PrincipalCollection principals = context.resolvePrincipals();boolean authenticated = context.resolveAuthenticated();//創建return new DelegatingSubject(principals, authenticated, session, sessionCreationEnabled, securityManager); } 復制代碼保存subject
/*** 具體保存邏輯* 由subjectDAO的實現類完成* @param subject*/ protected void save(Subject subject) {this.subjectDAO.save(subject); } 復制代碼/*** 默認的實現方式是把subject保存到session中* @param subject* @return*/ public Subject save(Subject subject) {if (isSessionStorageEnabled(subject)) {saveToSession(subject);} else {log.trace("Session storage of subject state for Subject [{}] has been disabled: identity and " +"authentication state are expected to be initialized on every request or invocation.", subject);}return subject; } 復制代碼/*** 保存登陸的信息和登陸的狀態* @param subject*/ protected void saveToSession(Subject subject) {mergePrincipals(subject);mergeAuthenticationState(subject); } 復制代碼protected void mergePrincipals(Subject subject) {PrincipalCollection currentPrincipals = null;if (currentPrincipals == null || currentPrincipals.isEmpty()) {currentPrincipals = subject.getPrincipals();}Session session = subject.getSession(false);/*** 如果有Session* 則把變動的PrincipalCollection保存進去*/if (session == null) {if (!isEmpty(currentPrincipals)) {session = subject.getSession();session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);}} else {PrincipalCollection existingPrincipals =(PrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);if (isEmpty(currentPrincipals)) {if (!isEmpty(existingPrincipals)) {session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);}} else {//只有修改過了的,才會被保存if (!currentPrincipals.equals(existingPrincipals)) {session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, currentPrincipals);}}} } 復制代碼/*** 刷新session中的登陸標記* @param subject*/ protected void mergeAuthenticationState(Subject subject) {Session session = subject.getSession(false);if (session == null) {if (subject.isAuthenticated()) {/*** 如果登陸成功,并且第一次訪問Session* 則會去創建也給新的*/session = subject.getSession();session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, Boolean.TRUE);}} else {/*** 如果有session* 則更新標記*/Boolean existingAuthc = (Boolean) session.getAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);if (subject.isAuthenticated()) {if (existingAuthc == null || !existingAuthc) {session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, Boolean.TRUE);}} else {if (existingAuthc != null) {session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);}}} } 復制代碼保存subject時,如何生成的session(如果開啟)
protected Session createSession(SessionContext context) throws AuthorizationException {return doCreateSession(context); } 復制代碼protected Session doCreateSession(SessionContext context) {/*** 使用工廠類創建session* 默認SimpleSession*/Session s = newSessionInstance(context);create(s);return s; } 復制代碼public Session createSession(SessionContext initData) {/*** 這里生成sessionId 可以嗎?*/return new SimpleSession(); } 復制代碼shiro默認使用MemorySessionDAO
protected void create(Session session) {if (log.isDebugEnabled()) {log.debug("Creating new EIS record for new session instance [" + session + "]");}//最終交由sessionDAO生成sessionDAO.create(session); } 復制代碼protected Serializable doCreate(Session session) {/*** sessionId最終在SessionDAO中生成* 默認使用java的uuid*/Serializable sessionId = generateSessionId(session);assignSessionId(session, sessionId);storeSession(sessionId, session);return sessionId; } 復制代碼修改session時,shiro是如何同步到本地的(或者HttpSession)
/*** 如果沒有session* 則創建* @param create* @return*/ @Override public Session getSession(boolean create) {if (log.isTraceEnabled()) {log.trace("attempting to get session; create = " + create +"; session is null = " + (this.session == null) +"; session has id = " + (this.session != null && session.getId() != null));}if (this.session == null && create) {//added in 1.2:if (!isSessionCreationEnabled()) { //沒開啟sessionCreationEnabled 會報錯String msg = "Session creation has been disabled for the current subject. This exception indicates " +"that there is either a programming error (using a session when it should never be " +"used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " +"for the current Subject. See the " + DisabledSessionException.class.getName() + " JavaDoc " +"for more.";throw new DisabledSessionException(msg);}/*** 創建的是DefaultSessionContext* 默認沒什么內容*/SessionContext sessionContext = createSessionContext();/*** 最終交由securityManager來生成Session*/Session session = this.securityManager.start(sessionContext);/*** 最終讓用戶操作的多次包裝的Session*/this.session = decorate(session);}return this.session; } 復制代碼 public Session start(SessionContext context) {/**.* 創建了一個SimpleSession*/Session session = createSession(context);onStart(session, context);/*** 創建對外暴露的Session* 其實就是代理了一下** 主要為了讓session在普通的操作下完成更多的功能*/return createExposedSession(session, context); } 復制代碼protected Session createExposedSession(Session session, SessionContext context) {//代理Sessionreturn new DelegatingSession(this, new DefaultSessionKey(session.getId())); } 復制代碼/*** 代理的session* 他會把對所有對session的操作* 提交給sessionManager去處理* @Author: lilingyan* @Date 2019/6/3 13:37*/ public class DelegatingSession implements Session {private final SessionKey key;private final transient NativeSessionManager sessionManager;@Overridepublic void setAttribute(Object attributeKey, Object value) throws InvalidSessionException {if (value == null) {removeAttribute(attributeKey);} else {//被代理了sessionManager.setAttribute(this.key, attributeKey, value);}}... 復制代碼//在SessionManager中代理的處理方法 public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException {if (value == null) {removeAttribute(sessionKey, attributeKey);} else {Session s = lookupRequiredSession(sessionKey);s.setAttribute(attributeKey, value);onChange(s);} } ... 復制代碼protected void onChange(Session session) {//最終任何修改都會被傳遞到sessionDAOsessionDAO.update(session); } 復制代碼轉載于:https://juejin.im/post/5cf4d92951882521bf340aa0
總結
以上是生活随笔為你收集整理的shiro登陆流程源码详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 初学Flink,对Watermarks的
- 下一篇: java 安全考虑