之前写的博客里都是使用.ini文件来获取信息的,包括用户信息,角色信息,权限信息等。进入系统时,都是从.ini文件这读取进入的。实际中除非这个系统特别特别简单,否则一般都不是这样干的,这些信息都是需要在数据库中进行维护的,所以就需要用到自定义realm了。
1. 数据库建表
首先在数据库中新建三个表:t_user,t_role和t_permission,分别存储用户信息,角色信息和权限信息,建表语句如下:
CREATE TABLE `t_role` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `rolename` varchar(20) DEFAULT NULL COMMENT '角色名称', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 CREATE TABLE `t_user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户主键', `username` varchar(20) NOT NULL COMMENT '用户名', `password` varchar(20) NOT NULL COMMENT '密码', `role_id` int(11) DEFAULT NULL COMMENT '外键关联role表', PRIMARY KEY (`id`), KEY `role_id` (`role_id`), CONSTRAINT `t_user_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 CREATE TABLE `t_permission` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `permissionname` varchar(50) NOT NULL COMMENT '权限名', `role_id` int(11) DEFAULT NULL COMMENT '外键关联role', PRIMARY KEY (`id`), KEY `role_id` (`role_id`), CONSTRAINT `t_permission_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
每个表中我添加了一些测试数据,如下:
2. 自定义realm
自定义realm中需要操作数据库,所有首先得先写一个dao,使用的是原始的jdbc,主要是下面的自定义realm。
public class UserDao { //根据用户名查找用户 public User getByUsername(Connection conn, String username) throws Exception { User resultUser = null; String sql = "select * from t_user where username=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ResultSet rs = ps.executeQuery(); if(rs.next()) { resultUser = new User(); resultUser.setId(rs.getInt("id")); resultUser.setUsername(rs.getString("username")); resultUser.setPassword(rs.getString("password")); } return resultUser; } //根据用户名查找改用户所拥有的角色 public Set<String> getRoles(Connection conn, String username) throws Exception { Set<String> roles = new HashSet<String>(); String sql = "select * from t_user u, t_role r where u.role_id=r.id and u.username=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ResultSet rs = ps.executeQuery(); while(rs.next()) { roles.add(rs.getString("rolename")); } return roles; } //根据用户名查找该用户角色所拥有的权限 public Set<String> getPerms(Connection conn, String username) throws Exception { Set<String> perms = new HashSet<String>(); String sql = "select * from t_user u, t_role r, t_permission p where u.role_id=r.id and p.role_id=r.id and u.username=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ResultSet rs = ps.executeQuery(); while(rs.next()) { perms.add(rs.getString("permissionname")); } return perms; } }
有了dao了,接下来就可以写自定义的realm了,自定义realm需要继承AuthorizingRealm类,因为该类封装了很多方法,它也是一步步继承自Realm类的,继承了AuthorizingRealm类后,需要重写两个方法:
doGetAuthenticationInfo()方法:用来验证当前登录的用户,获取认证信息
doGetAuthorizationInfo()方法:用来为当前登陆成功的用户授予权限和角色(已经登陆成功了)
下面来看一下具体的实现:
public class MyRealm extends AuthorizingRealm { private UserDao userDao = new UserDao(); // 为当前登陆成功的用户授予权限和角色,已经登陆成功了 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); //获取用户名 SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Connection conn = null; try { conn = DbUtil.getConnection(); authorizationInfo.setRoles(userDao.getRoles(conn, username)); //设置角色 authorizationInfo.setStringPermissions(userDao.getPerms(conn, username)); //设置权限 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { DbUtil.closeConnection(conn); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } return authorizationInfo; } // 验证当前登录的用户,获取认证信息 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); // 获取用户名 Connection conn = null; try { conn = DbUtil.getConnection(); User user = userDao.getByUsername(conn, username); // 仅仅是根据用户名查出的用户信息,不涉及到密码 if (user != null) { AuthenticationInfo authcInfo = new SimpleAuthenticationInfo( user.getUsername(), user.getPassword(), "myrealm"); return authcInfo; } else { return null; } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { DbUtil.closeConnection(conn); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; } }
从上面两个方法中可以看出:验证身份的时候是根据用户输入的用户名先从数据库中查出该用户名对应的用户,这时候并没有涉及到密码,也就是说到这一步的时候,即使用户输入的密码不对,也是可以查出来该用户的,然后将该用户的正确信息封装到authcInfo 中返回给Shiro,接下来就是Shiro的事了,它会根据这里面的真实信息与用户前台输入的用户名和密码进行校验, 这个时候也要校验密码了,如果校验通过就让用户登录,否则跳转到指定页面。同理,权限验证的时候也是先根据用户名获取与该用户名有关的角色和权限,然后封装到authorizationInfo中返回给Shiro。
3. 修改ini文件
在该配置文件中,[users]和[roles]的信息就可以删掉了,因为这些信息都是从数据库中维护的,另外还要在文件中指定我们自定义的realm的完全限定名,并且指定securityManager的realm使用我们自定义的realm,如下:
[main] authc.loginUrl=/login roles.unauthorizedUrl=/unauthorized.jsp perms.unauthorizedUrl=/unauthorized.jsp #定义自己的realm myRealm=demo.shiro.realm.MyRealm securityManager.realms=$myRealm #定义请求的地址需要做什么验证 [urls] /login=anon /admin=authc /student=roles[teacher] /teacher=perms["user:create"]
这样我们自定义的realm就搞定了,根据配置文件,当我们请求…/admin的时候会进行身份认证,所以会进入LoginServlet中,当调用currentUser.login(token);
的时候,就会进入我们自定义的realm中的doGetAuthenticationInfo方法进行身份初始化,然后交给Shiro去验证。当我们请求…./student的时候,也会先进行身份验证,就是上面的过程,然后验证通过,当我们再次请求…/student的时候,就会进入我们自定义的realm中的doGetAuthorizationInfo方法进行权限的初始化,然后交给Shiro去验证。
相关推荐
例如,通过`Realm`类实现与数据库或其他后端服务的交互,自定义身份验证和授权逻辑。 总的来说,Apache Shiro是一个全面的Java安全框架,提供了从身份验证到授权的全套解决方案,并且与多种技术和环境兼容。通过...
在"shiro-realm案例"中,我们将探讨如何自定义Realm来实现与应用程序特定的权限验证。 Realm在Shiro中扮演着核心角色,它是身份验证(Authentication)和授权(Authorization)的基础。 Realm可以看作是Shiro与...
Shiro的模块化设计允许开发者通过插件扩展其功能,如自定义认证策略、加密算法等。 10. **事件监听**: Shiro提供了事件监听机制,开发者可以通过实现Listener接口,对认证、授权等事件进行监听和处理。 11. **...
在这个名为“用于测试shiro中自定义Realm的测试代码”的项目中,我们主要关注的是如何自定义Realm来适应特定的认证和授权需求。 Realm在Shiro中扮演着核心角色,它是Shiro与应用程序特定的安全存储(如数据库、...
Shiro 的设计允许开发者自定义认证和授权策略,创建自己的 Realm 类来适应特定的数据源。此外,还可以通过实现特定接口或继承基类来自定义会话管理、事件处理等行为。 总之,"shiro-root-1.2.2-source-release.zip...
在Spring集成Shiro进行安全控制时,我们常常需要自定义Realm来实现权限验证与授权功能。然而,在实际操作中,可能会遇到一个问题:当我们在自定义的Realm类中使用注解(@Autowired)尝试注入Spring管理的Bean时,这些...
Apache Shiro 是一个强大且易用的Java...通过研究这些源码和示例,开发者可以更深入地了解Shiro的工作机制,并根据需求自定义安全策略。对于想要学习安全框架或者改进现有系统安全性的开发者来说,这是一个宝贵的资源。
开发者可以通过自定义 Realm 实现与数据库或其他数据源的交互,验证用户的身份信息。 2. **授权**:Shiro 的权限管理允许开发者细粒度地控制用户对资源的访问。它支持角色(Role)和权限(Permission)的概念,通过...
其中QueryDatabaseAuthenticationHandler这个类是自定义构建的,在cas/WEB-INF/lib/cas-jdbc-1.0.0.jar里面,有兴趣的同学可以发编译看下,关于几个属性的说明 1. dataSource: 数据源,配置MySQL的连接信息 2. ...
在Web应用中,自定义Realm是为了适配具体的应用场景,因为Shiro的默认Realm并不能满足所有需求。自定义Realm可以让我们更好地控制认证和授权的过程,以满足业务逻辑。以下将详细介绍如何在Shiro中创建自定义Realm。 ...
如果你的应用程序有自己的用户数据库,你可能需要创建一个自定义的Realm,继承自`AuthorizingRealm`,重写`doGetAuthenticationInfo`和`doGetAuthorizationInfo`方法,以实现与你数据库的交互。 10. **会话管理** ...
通过研究 `shiro-root-1.4.1-source-release.zip` 的源码,开发者可以深入了解 Shiro 如何实现这些功能,如何与其他系统集成,以及如何自定义 Shiro 来满足特定需求。此外,源码分析还能帮助开发者发现潜在的性能...
在这个“shiro权限框架自定义Realm示例”中,我们将深入理解Shiro的核心组件Realm以及如何根据实际需求对其进行定制。 Realm在Shiro中扮演着关键角色,它是Shiro与应用程序特定的安全数据源(如数据库、LDAP或文件...
本实例将通过自定义Realm来演示如何使用Shiro从数据库中查询数据进行用户验证。 首先,我们需要了解 Realm 的基本概念。在Shiro中, Realm 是一个接口,它实现了认证和授权的功能。默认的 Realm 类型包括 JdbcRealm...
例如,你可以查看如何实现自定义的Realm以对接自己的用户数据源,或者如何利用Shiro的Filter链来定制Web应用的安全控制。同时,对于熟悉Guice或Spring的开发者,理解Shiro与这些框架的集成方式也是非常有价值的。 ...
在"easyweb-shiro-master"中,开发者可以通过自定义Realm实现与数据库或其他数据源的交互,验证用户的登录信息。 2. **授权(Authorization)**:Shiro的授权功能允许开发者灵活地定义角色和权限,实现细粒度的访问...
2. **配置Shiro**:创建一个Shiro的配置类,定义Realm(认证和授权的实现),以及任何自定义的安全设置。例如,配置RememberMe服务,或自定义session的存储机制。 3. **整合Spring**:使用`DelegatingFilterProxy`...
在这个"shiro-mybatis-demo"项目中,开发者结合了Shiro与MyBatis,利用自定义的Realm实现了用户的身份认证和权限授权。 **Shiro 框架基础** Shiro 主要由以下三个核心组件构成: 1. **Subject**:Shiro 的核心...
8. **可扩展性**:Shiro的设计允许开发者自定义各种组件,如 Realm、Matcher 和 Authentication Strategy,以适应特定的安全需求。 在从1.3.2升级到1.6.0的过程中,Apache Shiro修复了许多已知的安全漏洞,增强了其...