Shiro | 实现权限验证完整版
写在前面的话
提及权限,就会想到安全,是一个十分棘手的话题。这里只是作为学校Shiro的一个记录,而不是,权限就应该这样设计之类的。
Shiro框架
1、Shiro是基于Apache开源的强大灵活的开源安全框架。
2、Shiro提供了 认证
,授权
,企业会话管理
、安全加密
、缓存管理
。
3、Shiro与Security对比
4、Shiro整体架构
5、特性
6、认证流程
认证
当我们理解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真正帮我们做的就是认证这一步,那他到底是如何去认证的呢?
我们把我们登录的代码完善一下:
/** * 登录操作,返回登录认证信息 * @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 "登录失败"; }
Realm
1、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"); }
2、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()); }
3、自定义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还是有很大帮助的,你觉得呢?
4、安全加密
明文密码?
好吧,我们看看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);
权限
1、Shiro为我们提供的权限
名称 | 说明 |
---|---|
anon | 不校验 |
authc | 作校验 |
roles | 需要具有指定角色(一个或多个)才能访问 |
perms | 需要具有指定角色(一个或多个)才能访问 |
2、配置
<!-- 从上往下开始匹配 --> /login.html = anon /subLogin = anon <!--/testRole = roles["admin"]--> <!--/testRole1 = roles["admin", "admin1"]--> <!--/testPerms = perms["user:delete"]--> <!--/testPerms1 = perms["user:delete", "user:update"]-->
3、自定义权限认证
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之后,我们怎么处理。什么意思?分布式系统,需要共享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; } }
缓存管理(Cache)
缓存管理同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安全框架入门
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
使WPF程序应用预置的控件风格, 如Aero, Luna, Royale, Classic等
原文: 使WPF程序应用预置的控件风格, 如Aero, Luna, Royale, Classic等 WPF预设有Aero, Classic, Luna, Royale主题, WPF程序会根据Windows主题决定WPF程序所使用的控件风格, 而且当Windows主题不是Aero, Luna或Royale, 而是其他主题的话, WPF将会采用比较丑的Classic主题, 那我怎么让WPF程序使用指定的主题呢? 其实很简单, 下面实例如何设定为Aero主题: 在WPF项目中添加PresentationFramework.Aero.dll这个引用, 然后在程序的Resources中加入 < Application .Resources > < ResourceDictionary Source ="/PresentationFramework.Aero,Version=3.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35;component/themes/aero.normalcolor.xaml" /...
- 下一篇
MSSQL · 最佳实践 · 使用混合密钥实现列加密
摘要 在SQL Server安全系列专题的上两期月报分享中,我们分别分享了:如何使用对称密钥实现SQL Server列加密技术和使用非对称密钥加密方式实现SQL Server列加密。本期月报我们分享使用混合密钥加密方式实现SQL Server列加密技术,最大限度减少性能损失,最大程度保护用户数据安全。 场景引入 对称加密是指加密和解密过程使用同一个密钥的加密算法,非对称加密是指加密和解密过程使用不同的密钥进行的加密算法。因此,通常来说对称加密安全性较弱,非对象加密安全性相对较高。凡事都具有两面性,非对称密钥加密的安全性较好,但通常算法相比对称密钥复杂许多,因此会带来性能上的损失也更大。有没有一种方法既可以最大限度保证数据安全性,又能够最大限度的减少性能损失呢?这便是本期月报分享的价值所在:SQL Server使用混合密钥实现列加密技术。
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker快速安装Oracle11G,搭建oracle11g学习环境