说起权限,就会想到安全,是一个十分棘手的话题。这里只是做为学校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安全框架入门