通过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是一种规范,词能达意,用户的添加权限

shiro三连斩之第一斩的更多相关文章

  1. shiro三连斩之第二斩(SSM)

    在SSM框架中使用shiro.环境 使用idea工具. 最主要的大概是配置文件如何配置吧. 1配置maven依赖 <?xml version="1.0" encoding=& ...

  2. shiro三连斩之第三斩,整合 springboot

    shiro爱springboot中使用 ,还有thymeleaf前端框架.主要是如何配置 pom.xml配置依赖 <?xml version="1.0" encoding=& ...

  3. shiro三连斩之概念

    1, 什么是Shiro? Shiro是一个安全框架,用于解决系统的认证和授权问题,同时提供了会话管理,数据加密,与WEB集成,缓存等机制. Authentication:身份认证/登录,验证用户是不是 ...

  4. 数据库时间内接受的是lang类型的时间 分为三种字段 第一种只存日期 第二种存日期+时间 第三种时间戳

    数据库时间内接受的是lang类型的时间 分为三种字段 第一种只存日期 第二种存日期+时间 第三种时间戳

  5. 《CLR.via.C#第三版》第一部分读书笔记(一)

    最近开始仔细研读<CLR.via.C#第三版>这本书.读pdf文档确实很累.建议有条件的朋友还是买书看吧. 我的笔记用来记录我对这本书的理解,简化下逻辑,对每个部分我觉得是要点的进行归纳总 ...

  6. 从零开始学ios开发(三):第一个有交互的app

    感谢大家的关注,也给我一份动力,让我继续前进.有了自己的家庭有了孩子,过着上有老下有小的生活,能够挤出点时间学习真的很难,每天弄好孩子睡觉已经是晚上10点左右了,然后再弄自己的事情,一转眼很快就到12 ...

  7. 【转】apue《UNIX环境高级编程第三版》第一章答案详解

    原文网址:http://blog.csdn.net/hubbybob1/article/details/40859835 大家好,从这周开始学习apue<UNIX环境高级编程第三版>,在此 ...

  8. 《跟我学Shiro》学习笔记 第一章:Shiro简介

    前言 现在在学习Shiro,参照着张开涛老师的博客进行学习,然后自己写博客记录一下学习中的知识点,一来可以加深理解,二来以后遗忘了可以查阅.没有学习过Shiro的小伙伴,也可以和我一起学习,大家共同进 ...

  9. 【Mysql优化三章】第一章

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/Bv5f4b8Peex/article/details/78130845 如今网上的好多mysql优化 ...

随机推荐

  1. 使用卷积神经网络CNN完成验证码识别

    gen_sample_by_captcha.py 生成验证码图片 # -*- coding: UTF-8 -*- """ 使用captcha lib生成验证码(前提:pi ...

  2. bzoj2054疯狂的馒头——线段树

    中文题面,一排有n个馒头,用刷子把整个连续的区间刷成一种颜色.因为颜色会覆盖掉之前的.所以我们可以用线段树来反着处理.如果这段区间之前刷到过就不要再遍历进去了,因为这次已经被上次刷的颜色给覆盖了.最后 ...

  3. vue+vuex 回退定位到初始位置

    先放出两张图(没错,你还在9012,做为一名资深设计师我唯一的技能点就是留白),简单说明下问题未做回退定位(从落地页回退,每次都回到A位置)想死啊有木有,每次都需要手动重新定位来选择,你大哥看到你做个 ...

  4. Go-延时函数defer

    关于延时调用函数(Deferred Function Calls)      延时调用函数基本语法如下: defer func_name(param-list) {} 当一个函数前有关键字 defer ...

  5. java在window下用cmd (javac、jar)命令行模拟Intellij IDEA软件生成jar包

    @@首先最重要的cmd命令: javac ,jar :使用如下(注意[.]不要输错): 1. javac编译: D:\MyWorkSet\idea_hadoop>javac -d .\out\p ...

  6. Scss 与 Sass 是什么,他们的区别在哪里?

    转载自:http://yunkus.com/difference-between-scss-sass/ 要想了解Scss 与 Sass 是什么以及他们的区别又在哪里,我们不过不先从他们各自的定义说起. ...

  7. 2019.4.24(js)

    1. 取得正数和负数的绝对值 Math.abs(7.25) Math.abs(-7.25) 2.利用JS刷新页面方法 https://www.cnblogs.com/Chen-XiaoJun/p/62 ...

  8. 多重线性回归 (multiple linear regression) | 变量选择 | 最佳模型 | 基本假设的诊断方法

    P133,这是第二次作业,考察多重线性回归.这个youtube频道真是精品,用R做统计.这里是R代码的总结. 连续变量和类别型变量总要分开讨论: 多重线性回归可以写成矩阵形式的一元一次回归:相当于把多 ...

  9. 使用Bootstrap Bar来增加Onboarding Progress Bar功能。

    git初始代码https://github.com/chentianwei411/at-mentions-with-action-text 首先,开分支onboardingbar. 然后, rails ...

  10. 小程序 新建项目底部tabbar

    在app.json中配置 { "pages": [ "pages/index/index", "pages/staging/staging" ...