zoukankan      html  css  js  c++  java
  • shiro三连斩之第一斩

    通过JavaSE,创建不同的 realm ,由简单到复杂一步步的深入的理解shiro完成认证与授权内在联系

    推荐从下向上一步步的测试,每一个方法都有详细的注释,说明  从哪里来-->到哪里去,理清其中的前因后果

    简单点的一下用户, 角色与权限之间的关系

    一个用户可以拥有某种角色或者身份,身份或者角色代表一种或多种权限

    一个用户可以拥有一种或多种权限,

    我理解为 角色或身份 做为用户的一个属性 来封装权限。用户最好不要直接拥有权限,而是作为某种角色拥有角色封装的权限

    package test;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.realm.SimpleAccountRealm;
    import org.apache.shiro.realm.jdbc.JdbcRealm;
    import org.apache.shiro.realm.text.IniRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.ByteSource;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class TestShiro {
    
    
        /**
         * 自定义一个域与业务层交互获取数据,真实数据的来源于业务层方法的调用,在域内定义 认证或者授权的方法
         */
        class CustomRealm extends AuthorizingRealm{
    
            /**
             * @param principals  与认证方法中返回的info或者本域有关
             * @return
             */
            @Override
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
                //从指定的域中获取subject提交过来的token,在认证方法中 参数1与本域进行关联,在这里取出,强转原参数1的类型
                //与这个本域关联的主题可能不止一个,.fromRealm()返回的集合,进行迭代后,获取。
                User user=(User)principals.fromRealm(this.getClass().getName()).iterator().next();
                String roleName = roleService.findRoleNameByUserName(user.getUsername);
                Set<String> perms = new HashSet<>();//用于存储 权限
                perms.add("权限1");//权限也是从数据库中查询出来的,这里直接手写
                perms.add("权限2");//权限是一种规范,目的用于判断的时候进行区别
                perms.add("权限3");//规范:user:list    user:delete  词能达意,对用户的查看权限,对用户的删除权限。
                SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();//把角色和权限封装为内部属性的pojo类
                info.addRole(roleName);
                info.setStringPermissions(perms);
                return info;
            }
            /**
             * @param token  controller层,根据当前用户 生成的 token 用来在这里与真实数据进行认证
             * @return
             * @throws AuthenticationException
             */
            @Override
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
                String login_userName = (String) token.getPrincipal();//返回controller层 subject.login()参数token中的username属性
                char[] password = (char[]) token.getCredentials();//这个token就是前面传的token ,在token内部用 string存uusername char[]存password
                String login_pwd = new String(password);//把 char数组类型转为字符串。
    
                //通过前台传送的token的username,调用自定义的业务成接口在数据库中查找,以username为用户名的用户信息
                User db_user=userService.fingUserByName(login_userName);
    
                //把数据库中查出来的信息,放入到info中以后,shiro会把数据库中的信息,与登录时生成的AuthenticationToken中的token进行比较,并把参数1与域进行关联。这个参数1可以在授权方法中获取
                //参数1:数据库中查询的真实数据,也属于域中的数据(一般为认证时需要用的数据) 参数2:密码,用于验证登录时token中的凭据 参数3:域的名称
                SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(db_user, db_user.getPassword(), this.getClass().getName());
                info.setCredentialsSalt(ByteSource.Util.bytes("盐值"));//每个用户的盐值都不一样,一般为数据库中查出来的,  info根据额外的数据对 token中的密码进行处理后再进行匹配
                return info;
            }
        }
        //自定义一个域,自定义认证的方式,授权的方式,
        private CustomRealm customRealm = new CustomRealm();
        /*该方法中,注册的自定义域中的方法需要与业务层进行配合,在数据库中进行查找,这里测试时失败的重点,从下往上,对不同的域一个个分析,理解shiro认证,授权的原理 核心*/
        @Test
        public void testCustomRealm() {
            DefaultSecurityManager securityManager = new DefaultSecurityManager();
            securityManager.setRealm(customRealm);
            SecurityUtils.setSecurityManager(securityManager);
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken("喜欢与改变", "admin");
            HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();//密码比较器,在CustomRealm重写的认证方法中的SimpleAuthenticationInfo这个对// 会对token中的密码根据密码比较器运算后数据库中的密码比较。数据库中的密码,也是这个比较器处理过
            matcher.setHashAlgorithmName("md5");//密码进行散列的方式
            matcher.setHashIterations(2);//散列次数,还有一个盐值,在CustomRealm中指定
            try {
                subject.login(token);
                System.out.println("成功");
            } catch (UnknownAccountException e) {
                System.out.println("用户名错误");
            } catch (AuthenticationException e) {
                System.out.println("认证失败");
            }
        }
    
    
    
    
    
        //把数据库作为数据域,需要给Realm一个连接的数据库的数据源
        private JdbcRealm jdbcRealm = new JdbcRealm();
        private DruidDataSource dataSource = new DruidDataSource();//连接数据库的数据源
        @Before
        public void druid() {//配置数据源数据并注入数据源
            dataSource.setUrl("jdbc:mysqljdbc://localhost/3306/数据库");
            dataSource.setUsername("root");
            dataSource.setUsername("");
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            jdbcRealm.setDataSource(dataSource);
        }
        /**
         * 认证 数据库中的数据作为域。真实的开发中,一般把真实的用户数据放在数据库,通过当前用户的 username 到数据库中查询 对应的 password
         * 把登录的密码与查询的密码进行比较(是否存在密码散列,对登录密码进一步操作后再比较)。登录成功,把用户信息,放在shiro的Session。
         */
        @Test
        public  void testJdbcRealm() {
            String sql = "select password from t_user where username= ?";
            jdbcRealm.setAuthenticationQuery(sql);//jdbc有默认的查询表格与对应的字段,可以手动设置,符合数据库结构的SQL语句
    
            DefaultSecurityManager securityManager = new DefaultSecurityManager();
            securityManager.setRealm(jdbcRealm);
            SecurityUtils.setSecurityManager(securityManager);
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken("喜欢与改变","admin");
            try {
                subject.login(token);//shiro根据token与指定的SQL语句。和查出来的数据进行操作
                System.out.println("认证成功");
    
            } catch (UnknownAccountException e) {
                System.out.println("用户名错误");
            } catch (AuthenticationException e) {
                System.out.println("认证失败");
    
            }
        }
    
    
    
    
        //把ini配置文件作为一个域,ini配置文件相当于数据库存储真实数据,IniRealm根据ini文件的中的信息,对当前用户进行认证与授权
        private IniRealm iniRealm = new IniRealm("classpath:authenticator.ini");
        /**
         * 认证。把ini配置文件作为域
         */
        @Test
        public  void  testIniRealm() {
            DefaultSecurityManager securityManager = new DefaultSecurityManager();
            securityManager.setRealm(iniRealm);
            SecurityUtils.setSecurityManager(securityManager);
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken("喜欢与改变", "admin");
            try {
                subject.login(token);//认证在IniRealm中(认证方法的中的接受参数的对象中)进行
                System.out.println("认证成功");
                System.out.println("是否有角色xxx"+subject.hasRole("superadmin"));
                System.out.println("是否有角色yyy"+subject.hasRole("normal"));
                subject.checkPermission("巨无霸");
                System.out.println("当前用户拥有  巨无霸  权限");
            } catch (UnknownAccountException e) {
                System.out.println("用户名错误");
            } catch (AuthenticationException e) {
                System.out.println("认证错误");
            }
        }
    
    
    
        //在controller层的写法一般是固定的,创建一个shiro核心控制器,给控制器注入域对象,并把控制器放到工具类中便于使用,从工具类中的获取一个subject对象
        //根据前台传来的岩心信息,生成一个token(令牌),放到subject.login()中,subject会保存这个令牌,进入核心控制器,核心控制器根据自己管理的(例,注入进来的域)对象,执行login()相关的操作
        //认证成功,可以继续执行下一步操作,我们一般对subject进行操作,查看这个用户的角色,拥有的权限等。这些信息,login()是核心控制器,已经完成了认证与授权
    
        //一个简单域(shiro自带的),用户传来数据 ,跟域中获取的数进行验证。(域相当于数据源)
        private SimpleAccountRealm realm = new SimpleAccountRealm();
        /**
         * 给域添加用户数据
         */
        @Before
        public void before() {
            realm.addAccount("喜欢与改变","admin");
        }
        /**
         * Authenticator 认证器
         * 认证  简单的域
         */
        @Test
        public void  textAuthenticator() {
            //1、创建shiro环境:shiro是核心控制器
            DefaultSecurityManager securityManager = new DefaultSecurityManager();
            //2、给核心控制器 注入域对象---->在认证过程中,存储安全数据(真实的用户信息,就是从这里获取数据和登录的进行验证)
            securityManager.setRealm(realm);
            SecurityUtils.setSecurityManager(securityManager);//把核心管理器放到工具类中
            //3、主体认证,会将认证委托给SecurityManager
            Subject subject = SecurityUtils.getSubject();
    
            //根据当前用户的信息生成一个token(令牌对象),用于让shiro进行验证
            UsernamePasswordToken token = new UsernamePasswordToken("喜欢与改变", "dmin");
            //执行认证
            try {
                subject.login(token);//shiro拿着token在realm中进行认证(realm可以自定义,再注册进来使用),认证失败抛出异常,根据抛出的异常可知道错误的范围
            } catch (UnknownAccountException e) {//用户名不匹配时抛出异常(还有别的异常类型,用是再查)
                System.out.println("当前信息不存在");
            } catch (AuthenticationException e) {//认证失败时抛出异常
                //e.printStackTrace();
                System.out.println("认证失败");
            }
        }
    }

     ini配置文件说明

    [users]
    zs=123,admin
    lisi=123,normal
    [roles]
    admin=user:insert,user:delete,user:list
    normal=user:list
    //下面是注释。
        users    账户=密码,角色(多个角色用逗号隔开)
        roles    角色=角色拥有的权限(多个权限用逗号隔开)
        角色拥有的权限,user:insert是一种规范,词能达意,用户的添加权限
  • 相关阅读:
    HTML DOM教程 14HTML DOM Document 对象
    HTML DOM教程 19HTML DOM Button 对象
    HTML DOM教程 22HTML DOM Form 对象
    HTML DOM教程 16HTML DOM Area 对象
    ubuntu 11.04 问题 小结
    VC6.0的 错误解决办法 小结
    boot.img的解包与打包
    shell里 截取字符串
    从零 使用vc
    Imagemagick 对图片 大小 和 格式的 调整
  • 原文地址:https://www.cnblogs.com/xiaoeyu/p/10459186.html
Copyright © 2011-2022 走看看