shiro的详细教程一

1.1 Shiro是什么一

• Apache Shiro 是 Java 的一个安全(权限)框架。
• Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在
JavaSE 环境,也可以用在 JavaEE 环境。
• Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存
等。
• 下载:http://shiro.apache.org/

1.2 功能简介
• 基本功能点如下图所示:

1.2.1 具体核心介绍

• Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
• Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用
户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户
对某个资源是否具有某个权限;
• Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有
信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
• Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
• Web Support:Web 支持,可以非常容易的集成到Web 环境;
• Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可
以提高效率;
• Concurrency:Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能
• 把权限自动传播过去;
• Testing:提供测试支持;
• Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
• Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登
录了

2.1Shiro 架构
我们从外部来看Shiro,即从应用程序角度的来观察如何使用Shiro完成工作。如下图:

可以看到:应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject;其每个API的含义:
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;
Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

   接下来我们来从Shiro内部来看下Shiro的架构,如下图所示:

Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;
SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;这样的话,比如我们在Web环境用,刚开始是一台Web服务器;接着又上了台EJB服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到Memcached服务器);
SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。

3.1pom引用

org.apache.shiro shiro-core 1.2.3 org.apache.shiro shiro-web 1.2.3 org.apache.shiro shiro-spring 1.2.3 org.apache.shiro shiro-ehcache 1.2.3 org.apache.shiro shiro-quartz 1.2.3

或者通过all引入和shiro相关的所有包

org.apache.shiro
shiro-all
1.2.3

4入门示例

使用shiro的基础认证功能(登录)

4.1创建shiro-first.ini
通过此配置文件创建securityManager工厂。
配置数据:
#对用户信息进行配置
[users]
#用户账号和密码
zhangsan=111111
lisi=222222

4.2入门程序代码
// 用户登陆和退出
@Test
public void testLoginAndLogout() {

    // 创建securityManager工厂,通过ini配置文件创建securityManager工厂
    Factory factory = new IniSecurityManagerFactory(
            "classpath:shiro-first.ini");

    //创建SecurityManager+
    SecurityManager securityManager = factory.getInstance();

    //将securityManager设置当前的运行环境中
    SecurityUtils.setSecurityManager(securityManager);

    //从SecurityUtils里边创建一个subject
    Subject subject = SecurityUtils.getSubject();

    //在认证提交前准备token(令牌)
    UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "111111");

    try {
        //执行认证提交
        subject.login(token);
    } catch (AuthenticationException e) {
        // 如果认证失败会shiro会上抛自定义的异常
        e.printStackTrace();
    }

    //是否认证通过
    boolean isAuthenticated =  subject.isAuthenticated();

    System.out.println("是否认证通过:" + isAuthenticated);

    //退出操作
    //subject.logout();

    //是否认证通过
   // isAuthenticated =  subject.isAuthenticated();

 //   System.out.println("是否认证通过:" + isAuthenticated);

}

5
认证之自定义realm(可以把Realm看成DataSource,即安全数据源。就是安全数据保存的位置,来源)
刚才的代码只是用于演示,因为真实情况不会把用户名密码挨个保存在ini文件中,实际开发需要realm从数据库中查询用户信息。

5.1realm接口及其实现类

5.2自定义realm

public class CustomRealm extends AuthorizingRealm {

// 设置realm的名称
@Override
public void setName(String name) {
    super.setName("customRealm");
}

// 用于认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
        AuthenticationToken token) throws AuthenticationException {

    // token是用户输入的
    // 第一步从token中取出身份信息
    String userCode = (String) token.getPrincipal();

    // 第二步:根据用户输入的userCode从数据库查询
    // ....


    // 如果查询不到返回null
    //数据库中用户账号是zhangsansan
    /*if(!userCode.equals("zhangsansan")){//
        return null;
    }*/


    // 模拟从数据库查询到密码
    String password = "111112";

    // 如果查询到返回认证信息AuthenticationInfo

    SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
            userCode, password, this.getName());

    return simpleAuthenticationInfo;
}

// 用于授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
        PrincipalCollection principals) {
    // TODO Auto-generated method stub
    return null;
}

5.3
配置realm,需要我们刚才自定义的realm生效,shiro的配置文件不是properties也不是xml而是.ini也是一种配置文件,类似属性文件
需要在shiro-realm.ini配置realm注入到securityManager中
[main]
#自定义realm
customerRealm=com.ty.realm.CustomRealm
#将realm设置到securityManager,相当于spring注入额
securityManager.realms=$customRealm

5.4
代码测试,与刚才入门程序代码一样,只是读取的ini配置文件路径改变

5.5认证流程

=================================

6 密码的散列算法加密
我们需要将用户的密码进行加密之后再保存到数据库,通常要对密码进行散列算法加密,常用的有md5、sha。
对MD5加密,如果知道散列后的值可以通过穷举法,得到MD5密码对应的明文。建议对MD5进行散列时加salt(盐),进行加密相当于对原始密码+盐 进行散列。

6.1使用shiro提供的md5代码
public static void main(String[] args) {
//原始 密码
String source = “111111”;
//盐
String salt = “qwerty”;
//散列次数
int hashIterations = 2;
//上边散列1次:f3694f162729b7d0254c6e40260bf15c
//上边散列2次:36f2dfa24d0a9fa97276abbe13e596fc
//构造方法中:
//第一个参数:明文,原始密码
//第二个参数:盐,通过使用随机数
//第三个参数:散列的次数,比如散列两次,相当 于md5(md5(’’))
Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations);
String password_md5 = md5Hash.toString();
System.out.println(password_md5);
//第一个参数:散列算法
SimpleHash simpleHash = new SimpleHash(“md5”, source, salt, hashIterations);
System.out.println(simpleHash.toString());

// Md5Hash是 SimpleHash的子类, SimpleHash除了md5以外还支持其他的散列算法
}

6.2 自行将散列使用在自定义realm中
正常使用时散列方法的思路:
在程序中对原始密码+盐进行散列,将散列值存储到数据库中,并且还要将盐也要存储在数据库中。
如果进行密码对比时,使用相同方法,将原始密码+盐进行散列,进行对比。

6.3在realm中配置凭证匹配器
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1
#将凭证匹配器设置到realm
customRealm=com.ty.shiro.realm.CustomRealmMd5
customRealm.credentialsMatcher= c r e d e n t i a l s M a t c h e r s e c u r i t y M a n a g e r . r e a l m s = credentialsMatcher securityManager.realms= credentialsMatchersecurityManager.realms=customRealm

6.4 session处理(这里只是简单介绍)
shiro提供的有会话管理功能,在web环境下可以用shiro提供的session功能代替原生web中的session
SecurityUtils.getSubject().getSession().setAttribute(obj,obj);
SecurityUtils.getSubject().getSession().getAttribute(obj,obj);

=================================
7
授权,也就是权限验证模块,判断用户是否拥有访问某个资源的权利

7.1授权方式
shiro支持三种方式的授权:

  • 编程式:通过写if/else授权代码块完成:
    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(“admin”)){
    //有权限
    }else{
    //无权限
    }

  • 注解方式:通过在执行的java 方法上放置相应的注解完成;
    @RequiresRoles(“admin”)
    public void hello(){
    //有权限
    }

  • jsp标签: 在jsp页面通过相应的标签完成:

7.1代码测试代码,只是将用户权限信息保存到ini文件中进行测试的体验demo,了解即可
7.1.1创建存放权限的配置文件shiro-permission.ini,里面的内容模拟保存在数据库。
[users]
#用户zhang的密码是123,此用户具有role1和role2两个角色
zhang=123,role1,role2
wang=123,role2

[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create

在ini文件中用户、角色、权限的配置规则是:“用户名=密码,角色1,角色2…” “角色=权限1,权限2…”,首先根据用户名找角色,再根据角色找权限,角色是权限集合。

7.1.2执行代码
认证之后便能够通过调用以下的代码执行授权功能:
try {
subject.login(token);
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 用户认证状态
Boolean isAuthenticated = subject.isAuthenticated();
System.out.println(“用户认证状态:” + isAuthenticated);
// 用户授权检测 基于角色授权
// 是否有某一个角色
System.out.println(“用户是否拥有一个角色:” + subject.hasRole(“role1”));
// 是否有多个角色
System.out.println(“用户是否拥有多个角色:” + subject.hasAllRoles(Arrays.asList(“role1”, “role2”)));
// subject.checkRole(“role1”);授权检测,失败则抛出异常
// subject.checkRoles(Arrays.asList(“role1”, “role2”));

    // 检查权限
    System.out.println("是否拥有某一个权限:" + subject.isPermitted("user:delete"));
    System.out.println("是否拥有多个权限:" + subject.isPermittedAll("user:create:1",    "user:delete"));

    //检查权限,若没有权限则上抛异常
    subject.checkPermission("sys:user:delete");
    subject.checkPermissions("user:create:1","user:delete");

8
授权中的自定义realm
与认证自定义realm一样,大部分情况是要从数据库获取权限数据,这里直接实现基于资源的授权。

8.1
在认证章节写的自定义realm类中我们发现又两个需要重写的方法,我们这里完善另一个doGetAuthorizationInfo方法,此方法用于授权,需要开发者完成:根据用户身份信息从数据库查询权限字符串,由shiro进行授权。

// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
// 获取身份信息
String username = (String) principals.getPrimaryPrincipal();
// 根据身份信息从数据库中查询权限数据
//…这里使用静态数据模拟,实际需要开发者自己执行sql查询判断

    //将权限信息封闭为AuthorizationInfo
 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

//权限
Set s = new HashSet();
s.add(“printer:print”);
s.add(“printer:query”);
simpleAuthorizationInfo.setStringPermissions(s);
//角色
Set r = new HashSet();
r.add(“role1”);
simpleAuthorizationInfo.setRoles®;
return simpleAuthorizationInfo;
}

8.2 shiro-realm.ini

ini配置文件不要忘了指向拥有自定义授权的realm

8.3 测试代码

同上边的授权测试代码,注意修改ini地址为shiro-realm.ini。

8.4 授权执行流程

你可能感兴趣的:(java基础)