【Shiro】Apache Shiro架构之自定义realm
【Shiro】Apache Shiro架构之身份认证(Authentication)
【Shiro】Apache Shiro架构之权限认证(Authorization)
【Shiro】Apache Shiro架构之集成web
【Shiro】Apache Shiro架构之实际运用(整合到Spring中)
之前写的博客里都是使用.ini文件来获取信息的,包括用户信息,角色信息,权限信息等。进入系统时,都是从.ini文件这读取进入的。实际中除非这个系统特别特别简单,否则一般都不是这样干的,这些信息都是需要在数据库中进行维护的,所以就需要用到自定义realm了。
写在前面:这篇博文是基于上一篇Shiro集成web基础之上修改的。
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"]
dbutil.java
package demo.shiro.servlet; import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties; public class DbUtil { private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
//static语句块初始化字段信息 static{
try{
//读取db.properties文件中的数据库连接信息 InputStream in = DbUtil.class.getClassLoader().getResourceAsStream("db.properties"); Properties prop = new Properties(); prop.load(in); //获取数据库连接驱动
driver = prop.getProperty("driver");
//获取数据库连接URL地址
url = prop.getProperty("url");
//获取数据库连接用户名
username = prop.getProperty("username");
//获取数据库连接密码
password = prop.getProperty("password");
//加载数据库驱动
Class.forName(driver); }catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
} /**
* @Method: getConnection
* @Description: 获取数据库连接对象
* @Anthor:
*
* @return Connection数据库连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, username,password);
} /**
* @Method: release
* @Description: 释放资源,
* 要释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
* @Anthor:孤傲苍狼
*
* @param conn
* @param st
* @param rs
*/
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
//关闭存储查询结果的ResultSet对象
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
//关闭负责执行SQL命令的Statement对象
st.close();
}catch (Exception e) {
e.printStackTrace();
}
} if(conn!=null){
try{
//关闭Connection数据库连接对象
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
try {
System.out.println(new DbUtil().getConnection().getClass().getName());
} catch (SQLException e) { e.printStackTrace();
}
} }
user.java
public class User { private Integer id;
private String username;
private String password;
//省略get set }
这样我们自定义的realm就搞定了,根据配置文件,当我们请求…/admin的时候会进行身份认证,所以会进入LoginServlet中,当调用currentUser.login(token);
的时候,就会进入我们自定义的realm中的doGetAuthenticationInfo方法进行身份初始化,然后交给Shiro去验证。当我们请求…./student的时候,也会先进行身份验证,就是上面的过程,然后验证通过,当我们再次请求…/student的时候,就会进入我们自定义的realm中的doGetAuthorizationInfo方法进行权限的初始化,然后交给Shiro去验证。
下一篇博文我将总结一下如何将Shiro运用到实际项目中,即如何将Shiro整合到spring中。
【Shiro】Apache Shiro架构之自定义realm的更多相关文章
- Shiro第二篇【介绍Shiro、认证流程、自定义realm、自定义realm支持md5】
什么是Shiro shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证.用户授权. spring中有spring security (原名Acegi),是一个权限框架,它和sp ...
- Apache Shiro 使用手册(一)Shiro架构介绍 - kdboy - ITeye技术网站
转载 原文地址 http://kdboy.iteye.com/blog/1154644 一.什么是Shiro Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理 ...
- Apache Shiro 使用手册(一)Shiro架构介绍
一.什么是Shiro Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户"登录": 授权 - ...
- 【Shiro】Apache Shiro架构之集成web
Shiro系列文章: [Shiro]Apache Shiro架构之身份认证(Authentication) [Shiro]Apache Shiro架构之权限认证(Authorization) [Shi ...
- 【Shiro】Apache Shiro架构之权限认证(Authorization)
Shiro系列文章: [Shiro]Apache Shiro架构之身份认证(Authentication) [Shiro]Apache Shiro架构之集成web [Shiro]Apache Shir ...
- 【Shiro】Apache Shiro架构之身份认证(Authentication)
Shiro系列文章: [Shiro]Apache Shiro架构之权限认证(Authorization) [Shiro]Apache Shiro架构之集成web [Shiro]Apache Shiro ...
- Apache Shiro 使用手册(一)Shiro架构介绍(转发:http://kdboy.iteye.com/blog/1154644#bc2399255)
一.什么是Shiro Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户“登录”: 授权 - 访问控制: 密码加密 ...
- Apache Shiro 手册
(一)Shiro架构介绍 一.什么是Shiro Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户"登录 ...
- Apache Shiro 使用手册---转载
原文地址:http://www.360doc.com/content/12/0104/13/834950_177177202.shtml (一)Shiro架构介绍 一.什么是Shiro Apache ...
随机推荐
- 给lnmp一键包中的nginx安装openresty的lua扩展
lnmp一键包(https://lnmp.org)本人在使用之后发现确实好用,能帮助我们快速搭建起lnmp.lamp和lnmpa的web生产环境,因此推荐大家可以多试试.但有的朋友可能需要使用open ...
- Problem B: 零起点学算法81——找出数组中最大元素的位置(下标值
#include<stdio.h> int main(void) { ],i,max; while(scanf("%d",&n)!=EOF) { ;i<n ...
- mysql服务常用命令
一.查出当前mysql正在执行的sql命令mysql> show full processlist; 然后找出要终止的语句的idmysql> kill id号 未登录的情况下>mys ...
- 在Visual Studio中开发Matlab mex文件,生成mexw64/mexw32
csunking贡献,2015-9-22 1712 1. 概述 通过使用C/C++与Matlab混合编程,既可以享受到C代码快速执行的速度,又可以方便的使用Matlab众多的库函数和强大的绘图功能 ...
- 【JSP JSTL】<c:if>多个判断条件 + <c:foreach>满足条件跳出循环
有一个需求,将所拥有的权限存放于session中,现在JSP页面判断这些如果在所有权限中有某一个或者某几个,就显示相对应的页面内容 举一个例子,实现以上的逻辑,<c:if>多个判断条件 + ...
- 【JSTL】<c:if test=“”>没有else的解决方法
后台封装了一个对象 放在model里: model.addAttribute("notice", notice); notice是个对象 然后前台如果没有公告的话,希望显示暂无公告 ...
- 对Emlog 6.0 Beta的完整代码审计过程
Emlog 6.0 beta版本,这可能是最后一篇关于PHP语言CMS的代码审计文章,此次将详细记录完整的审计过程. 文章基本上完整记录小东的对此CMS审计过程,或许显得繁琐,但代码审计的过程就是这样 ...
- xss bypass
重要的4个规则: 1 &符号不应该出现在HTML的大部分节点中. 2 尖括号<>是不应该出现在标签内的,除非为引号引用. 3 在text节点里面,<左尖括号有很大的危害. 4 ...
- django 模板实现换行
django中的模板并不能实现自动换行,遇到/n的话不会自动换行,毕竟/n是python里面的换行符,html怎么可能认识呢? 那如何实现在模板中换行呢 <p> {{ article.co ...
- python scikit-learn选择正确估算器
下图摘自官方文档 链接 http://scikit-learn.org/stable/tutorial/machine_learning_map/index.html