說起權限,就會想到安全,是一個十分棘手的話題。這裏只是做爲學校Shiro的一個記錄,而不是,權限就應該這樣設計之類的。html
一、Shiro是基於Apache開源的強大靈活的開源安全框架。java
二、Shiro提供了 認證
,受權
,企業會話管理
、安全加密
、緩存管理
。git
三、Shiro與Security對比github
四、Shiro總體架構web
五、特性sql
六、認證流程apache
當咱們理解Shiro以後,咱們就能比較容易梳理出認證流程,大概就是下面這樣子。緩存
咱們來看一段測試代碼:安全
// 1.構建SecurityManager環境 DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(simpleAccountRealm); // 2.主體提交認證請求 SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); // 3.認證 UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin"); subject.login(token); System.out.println("是否定證:" + subject.isAuthenticated()); // 4.退出 subject.logout(); System.out.println("是否定證:" + subject.isAuthenticated());
咱們發現Shiro真正幫咱們作的就是認證這一步,那他究竟是如何去認證的呢?session
咱們把咱們登陸的代碼完善一下:
/** * 登陸操做,返回登陸認證信息 * @return 登陸結果 */ public String login() { try { DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(simpleAccountRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin"); subject.login(token); if (subject.isAuthenticated()) return "登陸成功"; } catch (IncorrectCredentialsException e1) { e1.printStackTrace(); return "密碼錯誤"; } catch (LockedAccountException e2) { e2.printStackTrace(); return "登陸失敗,該用戶已被凍結"; } catch (AuthenticationException e3) { e3.printStackTrace(); return "該用戶不存在"; } catch (Exception e) { e.printStackTrace(); } return "登陸失敗"; }
一、IniReam
配置文件 user.ini
[users] Mark=admin,admin [roles] admin=user:add,user:delete,user:update,user:select
測試代碼
// IniRealm 測試 @Test public void testAuthenticationIniRealm () { IniRealm iniRealm = new IniRealm("classpath:user.ini"); DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(iniRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456"); subject.login(token); System.out.println("是否定證:" + subject.isAuthenticated()); subject.checkRole("admin"); subject.checkPermission("user:delete"); }
二、JdbcRealm
這裏有兩種方案,使用Shiro爲咱們提供了SQL語句,或者咱們本身寫SQL語句。
第一種:
// JdbcRealm 測試 Shiro SQL @Test public void testAuthenticationShiroSQL() { JdbcRealm jdbcRealm = new JdbcRealm(); jdbcRealm.setDataSource(druidDataSource); jdbcRealm.setPermissionsLookupEnabled(true); DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(jdbcRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("xfsy", "xfsy2018"); subject.login(token); System.out.println("是否定證:" + subject.isAuthenticated()); subject.checkRole("user"); subject.checkPermission("user:select"); }
第二種:
// JdbcRealm 測試 Custom SQL @Test public void testAuthenticationCustomSQL() { JdbcRealm jdbcRealm = new JdbcRealm(); jdbcRealm.setDataSource(druidDataSource); jdbcRealm.setPermissionsLookupEnabled(true); /** * @see org.apache.shiro.realm.jdbc.JdbcRealm */ String sql = "select pwd from t_user where user_name = ?"; jdbcRealm.setAuthenticationQuery(sql); DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(jdbcRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("test", "123"); subject.login(token); System.out.println("是否定證:" + subject.isAuthenticated()); }
三、自定義Realm
在上面咱們已經看過了 JdbcRealm
,因此咱們也能夠依葫蘆畫瓢自定義Ramlm。
第一步:繼承 AuthorizingRealm
第二步:實現認證方法
第三步:實現受權方法
經過AuthorizingRealm,咱們徹底按照本身的需求實現本身的業務邏輯。
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; /** * @author Wenyi Feng * @since 2018-10-22 */ public class CustomRealmTest extends AuthorizingRealm { /** * Retrieves the AuthorizationInfo for the given principals from the underlying data store. When returning * an instance from this method, you might want to consider using an instance of * {@link org.apache.shiro.authz.SimpleAuthorizationInfo SimpleAuthorizationInfo}, as it is suitable in most cases. * * @param principals the primary identifying principals of the AuthorizationInfo that should be retrieved. * @return the AuthorizationInfo associated with this principals. * @see org.apache.shiro.authz.SimpleAuthorizationInfo */ // 受權 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; } /** * Returns {@code true} if authentication caching should be utilized based on the specified * {@link AuthenticationToken} and/or {@link AuthenticationInfo}, {@code false} otherwise. * <p/> * The default implementation simply delegates to {@link #isAuthenticationCachingEnabled()}, the general-case * authentication caching setting. Subclasses can override this to turn on or off caching at runtime * based on the specific submitted runtime values. * * @param token the submitted authentication token * @param info the {@code AuthenticationInfo} acquired from data source lookup via * {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)} * @return {@code true} if authentication caching should be utilized based on the specified * {@link AuthenticationToken} and/or {@link AuthenticationInfo}, {@code false} otherwise. * @since 1.2 */ // 認證 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { return null; } }
可能Shiro提供的Realm並不能知足咱們的實際開發需求,因此真正弄明白自定義Realm仍是有很大幫助的,你以爲呢?
四、安全加密
明文密碼?
好吧,咱們看看Shiro爲咱們提供的加密方法。
// Md5Hash md5Hash = new Md5Hash("123456"); Md5Hash md5Hash = new Md5Hash("123456", "admin"); System.out.println(md5Hash.toString());
那咱們該怎麼使用了?
在Realm認證中設置鹽值
// 使用admin對密碼進行加密 authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("admin"));
咱們只須要告訴咱們的Realm,須要對密碼進行加密就能夠了。
CustomRealm customRealm = new CustomRealm(); HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); // 加密類型 matcher.setHashAlgorithmName("md5"); // 加密次數 matcher.setHashIterations(1); customRealm.setCredentialsMatcher(matcher);
一、Shiro爲咱們提供的權限
名稱 | 說明 |
---|---|
anon | 不校驗 |
authc | 做校驗 |
roles | 須要具備指定角色(一個或多個)才能訪問 |
perms | 須要具備指定角色(一個或多個)才能訪問 |
二、配置
<!-- 從上往下開始匹配 --> /login.html = anon /subLogin = anon <!--/testRole = roles["admin"]--> <!--/testRole1 = roles["admin", "admin1"]--> <!--/testPerms = perms["user:delete"]--> <!--/testPerms1 = perms["user:delete", "user:update"]-->
三、自定義權限認證
import org.apache.shiro.web.filter.authz.AuthorizationFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * @author Wenyi Feng * @since 2018-10-22 */ public class CustomAuthorizationFilter extends AuthorizationFilter { protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { return false; } }
另外,看一下 Shiro Filter
會話管理,就是拿到Session以後,咱們怎麼處理。什麼意思?分佈式系統,須要共享Session。
import org.apache.shiro.session.Session; import org.apache.shiro.session.UnknownSessionException; import org.apache.shiro.session.mgt.eis.AbstractSessionDAO; import java.io.Serializable; import java.util.Collection; /** * 自定義Session操做接口 * @author Wenyi Feng * @since 2018-10-22 */ public class CustomSessionDAO extends AbstractSessionDAO { // 建立Session protected Serializable doCreate(Session session) { return null; } // 讀session protected Session doReadSession(Serializable sessionId) { return null; } // 修改session public void update(Session session) throws UnknownSessionException { } // 刪除session public void delete(Session session) { } // 獲取當前活動的session public Collection<Session> getActiveSessions() { return null; } }
緩存管理同Session管理,能夠這樣說,session是一套系統的基礎,緩存決定系統的優化級別,很重要。
另外,咱們看一下,Shiro爲咱們設計的緩存接口。
package org.apache.shiro.cache; import java.util.Collection; import java.util.Set; public interface Cache<K, V> { V get(K var1) throws CacheException; V put(K var1, V var2) throws CacheException; V remove(K var1) throws CacheException; void clear() throws CacheException; int size(); Set<K> keys(); Collection<V> values(); }
咱們只須要爲token設置RememberMe就能夠了。
token.setRememberMe(user.getRememberMe());
[1] Shiro安全框架入門