zoukankan      html  css  js  c++  java
  • Apcahe Shiro学习笔记(二):通过JDBC进行权限控制

    一、概述:

      官方对Realm(领域)的描述:https://www.infoq.com/articles/apache-shiro

      

      其功能本质上是一个安全特定的DAO,用于链接数据持久层(任何形式的都可以:数据库、properties文件,xml文件等),获取数据给Shiro使用。

    二、数据库的搭建:

      创建数据库及表:

    DROP DATABASE IF EXISTS `apptest`;
    CREATE DATABASE `apptest` DEFAULT CHARACTER SET utf8 ;
    
    USE `apptest` ;
    
    DROP TABLE IF EXISTS `tb_customer`;
    CREATE TABLE `tb_customer` (
      `col_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
      `col_loginName` varchar(50) DEFAULT NULL COMMENT '登录名',
      `col_password` varchar(128) DEFAULT NULL COMMENT '密码',
      PRIMARY KEY (`col_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表';
    
    DROP TABLE IF EXISTS `tb_role`;
    CREATE TABLE `tb_role` (
      `col_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
      `col_roleName` varchar(45) DEFAULT NULL COMMENT '角色名称',
      PRIMARY KEY (`col_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='角色表';
    
    DROP TABLE IF EXISTS `tb_limit`;
    CREATE TABLE `tb_limit` (
      `col_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
      `col_limitName` varchar(45) DEFAULT NULL COMMENT '权限名称',
      PRIMARY KEY (`col_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='权限表';
    
    DROP TABLE IF EXISTS `tb_ref_customer_role`;
    CREATE TABLE `tb_ref_customer_role` (
      `col_customerId` int(11) NOT NULL COMMENT '用户主键id',
      `col_roleId` int(11) NOT NULL COMMENT '角色主键id',
      PRIMARY KEY (`col_customerId`,`col_roleId`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色管理表';
    
    DROP TABLE IF EXISTS `tb_ref_role_limit`;
    CREATE TABLE `tb_ref_role_limit` (
      `col_roleId` int(11) NOT NULL COMMENT '角色主键id',
      `col_limitId` int(11) NOT NULL COMMENT '权限主键id',
      PRIMARY KEY (`col_roleId`,`col_limitId`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色权限管理表';

      插入测试数据:

    USE `apptest` ;
    #新增一个用户
    INSERT INTO `apptest`.`tb_customer`(`col_loginName`,`col_password`)VALUES('sunnywen','111111');
    #新增一个角色
    INSERT INTO `apptest`.`tb_role`(`col_roleName`)VALUES('admin');
    #新增4个权限
    INSERT INTO `apptest`.`tb_limit`(`col_limitName`)VALUES('admin:create');
    INSERT INTO `apptest`.`tb_limit`(`col_limitName`)VALUES('admin:update');
    INSERT INTO `apptest`.`tb_limit`(`col_limitName`)VALUES('admin:query');
    INSERT INTO `apptest`.`tb_limit`(`col_limitName`)VALUES('admin:delete');
    #插入用户角色关联表
    INSERT INTO `apptest`.`tb_ref_customer_role`(`col_customerId`,`col_roleId`)VALUES(1,1);
    #插入角色权限管理表
    INSERT INTO `apptest`.`tb_ref_role_limit`(`col_roleId`,`col_limitId`)VALUES(1,1);
    INSERT INTO `apptest`.`tb_ref_role_limit`(`col_roleId`,`col_limitId`)VALUES(1,2);
    INSERT INTO `apptest`.`tb_ref_role_limit`(`col_roleId`,`col_limitId`)VALUES(1,3);

      最后结果:

    三、Java代码的实现:

       3.1、新建一个maven工程

      pom.xml文件如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.yoki.edu</groupId>
        <artifactId>ShiroLearn</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <version.base>1.0-RELEASE</version.base>
            <version.auth.shiro>1.2.3</version.auth.shiro>
            <version.logger.log4j>1.2.9</version.logger.log4j>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.41</version>
            </dependency>
            <!-- configure shiro -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>${version.auth.shiro}</version>
            </dependency>
            <!-- configure logging -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.9</version>
            </dependency>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.1.3</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>1.7.25</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.25</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.12</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.2.2</version>
            </dependency>
        </dependencies>
    
    </project>

       非maven工程的参考jar包如下:

      目录结构:

      3.2、创建实体类:

      用户实体类LoginAccount

    package org.yoki.edu;
    
    /**
     * Created by SunnyWen on 2017/7/3.
     */
    public class LoginAccount {
    
        private Integer id ;
        //用户登录名
        private String loginName ;
        //用户登录密码
        private String password ;
    
        /*
         * 省略setter、getter方法
         */    
    }

      角色实体类Role

      

    package org.yoki.edu;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by SunnyWen on 2017/7/3.
     */
    public class Role {
    
        private Integer id ;
        //角色名称
        private String roleName ;
        //权限名称List
        private List<String> limitList = new ArrayList<>();
    /* * 省略setter、getter方法 */ }

      3.3、创建数据库连接工具BusinessManager.java:

    package org.yoki.edu;
    
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by SunnyWen on 2017/7/3.
     */
    public class BusinessManager {
    
        private static final String driver = "com.mysql.jdbc.Driver";
        private static final String url = "jdbc:mysql://localhost:3306/apptest?Unicode=true&characterEncoding=UTF-8";
        private static final String user = "root";
        private static final String password = "root";
    
        private static Connection connection ;
    
        static{
            try {
                Class.forName(driver);
                connection = DriverManager.getConnection(url , user, password);
                System.out.println("Connect database success !!!");
            }catch (Exception e){
                System.out.println("Connect database failure !!!");
                e.printStackTrace();
            }
    
        }
    
        public static Connection getConnection(){
            if(null == connection){
                synchronized (Connection.class){
                    if(null == connection) {
                        connection = buildConnection();
                    }
                }
            }
            return connection ;
        }
    
        private static Connection buildConnection(){
            Connection connection = null ;
            try {
                Class.forName(driver);
                connection = DriverManager.getConnection(url , user, password);
            }catch (Exception e){
                e.printStackTrace();
            }
            return connection ;
        }
    
        /**
         * 根据用户名获取角色及角色下的权限
         * @param name
         * @return
         */
        public List<Role> listRoleByUserName(String name){
            StringBuffer sql1 = new StringBuffer() ;
            List<Role> list = new ArrayList<>() ;
            sql1.append("select col_id , col_roleName from tb_role where col_id in( ") ;
            sql1.append(" select distinct(col_roleId) from tb_ref_customer_role where col_customerId in ( " ) ;
            sql1.append("     select col_id from tb_customer where col_loginName = ? " ) ;
            sql1.append(" )" ) ;
            sql1.append(") ;") ;
            Connection connection = BusinessManager.getConnection() ;
            try {
                PreparedStatement statement = connection.prepareStatement(sql1.toString());
                statement.setString(1 , name);
                ResultSet set = statement.executeQuery() ;
                while(set.next()){
                    Role role = new Role() ;
                    role.setId(set.getInt(1)) ;
                    role.setRoleName(set.getString(2)); ;
                    //根据角色ID获取权限名称
                    StringBuffer sql2 = new StringBuffer() ;
                    sql2.append("select col_limitName from tb_limit where col_id in (" ) ;
                    sql2.append("   select distinct(col_limitId) from tb_ref_role_limit where col_roleId = ?" ) ;
                    sql2.append(")" ) ;
                    statement = connection.prepareStatement(sql2.toString());
                    statement.setInt(1 , role.getId());
                    ResultSet set2 = statement.executeQuery() ;
                    while (set2.next()){
                        role.getLimitList().add(set2.getString(1));
                    }
                    list.add(role);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return list ;
        }
    
        /**
         * 根据用户名获取用户
         * @param name
         * @return
         */
        public LoginAccount getLoginAccount(String name){
            StringBuffer sb = new StringBuffer() ;
            LoginAccount loginAccount = null ;
            sb.append("select col_loginName , col_password from tb_customer where col_loginName = ?") ;
            Connection connection = BusinessManager.getConnection() ;
            try {
                PreparedStatement statement = connection.prepareStatement(sb.toString());
                statement.setString(1 , name);
                ResultSet set = statement.executeQuery() ;
                if(set.next()){
                    loginAccount = new LoginAccount() ;
                    loginAccount.setLoginName(set.getString(1));
                    loginAccount.setPassword(set.getString(2));
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return loginAccount ;
        }
    
    }

      3.4、继承Realm,实现用户的认证授权:

      CustomSecurity.java

    package org.yoki.edu;
    
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import java.util.Collection;
    import java.util.List;
    
    /**
     * Created by SunnyWen on 2017/7/3.
     * 继承AuthorizingRealm
     */
    public class CustomSecurityRealm extends AuthorizingRealm {
    
        //数据库链接工具
        private BusinessManager businessManager = new BusinessManager();
    
        /**
         * 获取用户的授权信息
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) {
            String username = (String) principals.fromRealm(getName()).iterator().next();
            if (username != null) {
                // 查询用户授权信息
                 Collection<Role> pers = businessManager.listRoleByUserName(username);
                if (pers != null && !pers.isEmpty()) {
                     SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
                    for (Role role : pers){
                        //加入角色
                        info.addRole(role.getRoleName());
                        List<String> limitList = role.getLimitList() ;
                        for(String s : limitList){
                            //加入权限
                            info.addStringPermission(s);
                        }
                    }
                    return info;
                }
            }
            return null;
        }
    
        /**
         * 获取用户的认证信息
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken authcToken) throws AuthenticationException {
            //用户名密码Token
            UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
            // 通过表单接收的用户名
             String username = token.getUsername();
             if (username != null && !"".equals(username)) {
                 //获取数据库中的用户
                 LoginAccount account = businessManager.getLoginAccount(username);
                 if (account != null) {
                     return new SimpleAuthenticationInfo(
                             account.getLoginName(), account.getPassword(), getName());
                 }
             }
             return null;
        }
    
      
      /**
       * 用户认证方式,方法为父类org.apache.shiro.realm.AuthenticatingRealm的方法<BR>
       * 复写此方法可以更改认证方式
       * @param token
       * @param info
       * @throws AuthenticationException
       */
      @Override
      protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
        //你可以实现自己的AuthenticationToken,如下
        //if(token instanceof MyAuthenticationToken){
        //  MyAuthenticationToken myToken = (MyAuthenticationToken)token ;
        //  if(myToken.isWeChatLoginFlag()){
        //    String openId = myToken.getOpenId() ;
        //    Customer customer = Customer.selectOneByOpenId(openId);
        //    if(null == customer){
        //      String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
        //      throw new IncorrectCredentialsException(msg);
        //    }
        //    return;
        //  }
        //}
    
        //下方为源码,使用MD5校验方式校验用户名密码
          CredentialsMatcher cm = getCredentialsMatcher();
          if (cm != null) {
              if (!cm.doCredentialsMatch(token, info)) {
                  //not successful - throw an exception to indicate this:
                  String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
                  throw new IncorrectCredentialsException(msg);
              }
          } else {
              throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
                    "credentials during authentication.  If you do not wish for credentials to be examined, you " +
                    "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
          }
      }
    }

       shiro.ini配置文件

    [main]
    customSecurityRealm=org.yoki.edu.CustomSecurityRealm
    #配置SecurityManager的realm,可以配置多个,使用逗号隔开
    securityManager.realms=$customSecurityRealm

      3.5、测试:

    package org.yoki.edu;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    
    /**
     * Created by SunnyWen on 2017/7/3.
     */
    public class MainTest {
    
        public static void main(String[] args) {
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            Subject currentUser = SecurityUtils.getSubject();
            if (!currentUser.isAuthenticated()) {
                UsernamePasswordToken token = new UsernamePasswordToken("sunnywen", "222222");
                token.setRememberMe(true);
                try {
                    currentUser.login(token);
                } catch (UnknownAccountException uae) {
                    System.out.println("用户: " + token.getPrincipal() + " 不存在!!!");
                } catch (IncorrectCredentialsException ice) {
                    System.out.println("用户: " + token.getPrincipal() + " 密码错误!!!");
                    try {
                        System.out.println("用户: " + token.getPrincipal() + " 再次尝试登陆!!!");
                        token = new UsernamePasswordToken("sunnywen", "111111");
                        token.setRememberMe(true);
                        currentUser.login(token);
                    } catch (UnknownAccountException uae) {
                        System.out.println("用户: " + token.getPrincipal() + " 不存在!!!");
                    } catch (IncorrectCredentialsException ice2) {
                        System.out.println("用户: " + token.getPrincipal() + " 密码错误!!!");
                    } catch (LockedAccountException lae) {
                        System.out.println("用户: " + token.getPrincipal() + " 已经被冻结!!!");
                    } catch (AuthenticationException ae) {
                    }
                } catch (LockedAccountException lae) {
                    System.out.println("用户: " + token.getPrincipal() + " 已经被冻结!!!");
                } catch (AuthenticationException ae) {
                }
            }
            if(null != currentUser.getPrincipal())
                System.out.println("用户: " + currentUser.getPrincipal() + " 登录成功!!!");
    
            if (currentUser.hasRole("admin")) {
                System.out.println("用户: " + currentUser.getPrincipal() + " 拥有角色'admin'");
            } else {
                System.out.println("对不起,用户: " + currentUser.getPrincipal() + " 尚未拥有角色'admin'");
            }
    
            if (currentUser.isPermitted("admin:create")) {
                System.out.println("用户: " + currentUser.getPrincipal() + " 拥有权限'admin:create'");
            } else {
                System.out.println("对不起,用户: " + currentUser.getPrincipal() + " 尚未拥有权限'admin:create'");
            }
    
            if (currentUser.isPermitted("admin:update")) {
                System.out.println("用户: " + currentUser.getPrincipal() + " 拥有权限'admin:update'");
            } else {
                System.out.println("对不起,用户: " + currentUser.getPrincipal() + " 尚未拥有权限'admin:update'");
            }
    
            if (currentUser.isPermitted("admin:query")) {
                System.out.println("用户: " + currentUser.getPrincipal() + " 拥有权限'admin:query'");
            } else {
                System.out.println("对不起,用户: " + currentUser.getPrincipal() + " 尚未拥有权限'admin:query'");
            }
    
            if (currentUser.isPermitted("admin:delete")) {
                System.out.println("用户: " + currentUser.getPrincipal() + " 拥有权限'admin:delete'");
            } else {
                System.out.println("对不起,用户: " + currentUser.getPrincipal() + " 尚未拥有权限'admin:delete'");
            }
    
            currentUser.logout();
            try {
                Thread.sleep(500);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
    }

      结果:

    Connect database success !!!
    用户: sunnywen 密码错误!!!
    用户: sunnywen 再次尝试登陆!!!
    用户: sunnywen 登录成功!!!
    用户: sunnywen 拥有角色'admin'
    用户: sunnywen 拥有权限'admin:create'
    用户: sunnywen 拥有权限'admin:update'
    用户: sunnywen 拥有权限'admin:query'
    对不起,用户: sunnywen 尚未拥有权限'admin:delete'

    转载请标明转载出处 : https://i.cnblogs.com/EditPosts.aspx?postid=7115108

  • 相关阅读:
    poj 3468 A Simple Problem with Integers (线段树区间更新求和lazy思想)
    hdu 1166 敌兵布阵(线段树区间求和)
    队列和栈
    完数的输出
    数据类型
    ASP.NET 图片上传工具类 upload image简单好用功能齐全
    ASP.NET 文件上传类 简单好用
    将form表单元素转为实体对象 或集合 -ASP.NET C#
    .NET框架面向对象分层的个人想理
    .NET VS2012 将代码同步上传到 oschina.net 和 github
  • 原文地址:https://www.cnblogs.com/FlyingPuPu/p/7115108.html
Copyright © 2011-2022 走看看