【Shiro】Apache Shiro架构之自定义realm

  之前写的博客里都是使用.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"]

  这样我们自定义的realm就搞定了,根据配置文件,当我们请求…/admin的时候会进行身份认证,所以会进入LoginServlet中,当调用currentUser.login(token);的时候,就会进入我们自定义的realm中的doGetAuthenticationInfo方法进行身份初始化,然后交给Shiro去验证。当我们请求…./student的时候,也会先进行身份验证,就是上面的过程,然后验证通过,当我们再次请求…/student的时候,就会进入我们自定义的realm中的doGetAuthorizationInfo方法进行权限的初始化,然后交给Shiro去验证。

  下一篇博文我将总结一下如何将Shiro运用到实际项目中,即如何将Shiro整合到Spring中。



—–乐于分享,共同进步!

—–我的博客主页:http://blog.csdn.net/eson_15

时间: 2024-10-11 18:49:14

【Shiro】Apache Shiro架构之自定义realm的相关文章

Apache Shiro 使用手册(一)Shiro架构介绍

一.什么是Shiro Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户"登录": 授权 - 访问控制: 密码加密 - 保护或隐藏数据防止被偷窥: 会话管理 - 每用户相关的时间敏感的状态. 对于任何一个应用程序,Shiro都可以提供全面的安全管理服务.并且相对于其他安全框架,Shiro要简单的多. 二.Shiro的架构介绍 首先,来了解一下Shiro的三个核心组件:Subject. Security

shiro自定义realm支持MD5算法(六)

1.1     散列算法 通常需要对密码 进行散列,常用的有md5.sha, 对md5密码,如果知道散列后的值可以通过穷举算法,得到md5密码对应的明文. 建议对md5进行散列时加salt(盐),进行加密相当 于对原始密码+盐进行散列.(盐就相当于加入一个随机数) 正常使用时散列方法: 在程序中对原始密码+盐进行散列,将散列值存储到数据库中,并且还要将盐也要存储在数据库中. 如果进行密码对比时,使用相同 方法,将原始密码+盐进行散列,进行比对. 1.2 MD5测试 package cn.qlq.

Apache Shiro系列四:Shiro的架构

Shiro的设计目标就是让应用程序的安全管理更简单.更直观. 软件系统一般是基于用户故事来做设计.也就是我们会基于一个客户如何与这个软件系统交互来设计用户界面和服务接口.比如,你可能会说:“如果用户登录了我们的系统,我就给他们显示一个按钮,点击之后可以查看他自己的账户信息.如果没有登录,我就给他显示一个注册按钮.” 上述应用程序在很大程度上是为了满足用户的需求而编写的,即便这个“用户”不是人,而是一个其他的软件系统.你仍然是按照谁当前正在与你的系统交互的逻辑来编写你的逻辑代码. Shiro的设计

shiro学习笔记_0600_自定义realm实现授权

博客shiro学习笔记_0400_自定义Realm实现身份认证 介绍了认证,这里介绍授权. 1,仅仅通过配置文件来指定权限不够灵活且不方便.在实际的应用中大多数情况下都是将用户信息,角色信息,权限信息 保存到了数据库中.所以需要从数据库中去获取相关的数据信息.可以使用 shiro 提供的JdbcRealm来实现,,也可以自定义realm来实现.使用jdbcRealm往往也不够灵活.所以在实际应用中大多数情况都是自定义Realm来实现. 2,自定义Realm 需要继承 AuthorizingRea

shiro自定义realm(五)

上一节介绍了realm的作用: realm:需要根据token中的身份信息去查询数据库(入门程序使用ini配置文件),如果查到用户返回认证信息,如果查询不到返回null.token就相当于是对用户输入的用户名和密码的一个封装.下面就是创建一个用户名密码token: UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "111111"); Realm结构: 自定义realm packa

Shiro自定义Realm时用注解的方式注入父类的credentialsMatcher

用Shiro做登录权限控制时,密码加密是自定义的. 数据库的密码通过散列获取,如下,算法为:md5,盐为一个随机数字,散列迭代次数为3次,最终将salt与散列后的密码保存到数据库内,第二次登录时将登录的令牌再进行同样的运算后再与数据库的做对比. String algorithmName = "md5";String userName = "rose";String password = "rose123";int hashIterations =

Apache Shiro学习笔记(三)用户授权自定义Permission

鲁春利的工作笔记,好记性不如烂笔头 Shiro配置文件(shiro-customize-permission.ini) [main] myRealmA=com.invicme.apps.shiro.permission.MyRealmOne myPermissionResolver=com.invicme.apps.shiro.permission.MyPermissionResolver securityManager.authorizer.permissionResolver = $myPe

Shiro Review——自定义Realm实现认证

一,自定义Realm 在之前,使用过下面的ini文件进行认证测试: #对用户信息进行配置 [users] #用户名跟密码 zhangsan=111111 lisi=111111 里面用户的认证信息是写死的,so,now ,来测试下使用自定义Realm来从我们的DB读取User信息,完成用户认证. 首先大致看下Realm的类层级关系: 比如,我们之前使用ini文件中的users配置用户名跟密码的时候,认证和时候使用的Realm就是IniRealm,比如里面的JdbcRealm,我们可以在数据库里面

shiro授权及自定义realm授权(七)

1.授权流程 2.    三种授权方法 Shiro 支持三种方式的授权: 编程式:通过写if/else 授权代码块完成: Subject subject = SecurityUtils.getSubject(); if(subject.hasRole("admin")) { //有权限 } else { //无权限 } 注解式:通过在执行的Java方法上放置相应的注解完成: @RequiresRoles("admin") public void hello() {