shiro入门与认证原理

一、shiro介绍

1、什么是shiro
 shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。
2、shiro的优点
 (1)shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。
 (2)shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。
 (3)java领域中spring security(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro。
3、shiro的架构

 (1)Subject
  Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

 (2)SecurityManager
  SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
   SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。

 (3)Authenticator
  Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
 (4)Authorizer
  Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

 (5)realm
  Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。

 (6)sessionManager
  sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
 (7)SessionDAO
  SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。
 (8)CacheManager
  CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
 (9)Cryptography
  Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
4.依赖jar包
(1)maven方式

<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-web</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-ehcache</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-quartz</artifactId>
			<version>1.2.3</version>
		</dependency>

//也可以通过引入shiro-all包括shiro所有的包:
	<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-all</artifactId>
			<version>1.2.3</version>
		</dependency>

(2)导入方式的jar包

二、shiro认证

1、认证流程

2、入门程序(用户登陆和退出)
创建一个Java工程,其目录结构如下

(1)创建shiro_first.ini文件,通过此配置文件创建securityManager工厂。

#对用户信息进行配置
[users]
#用户名和密码
zhangsan=111111
lisi=111111

(2)测试代码

 public void test1(){
        //创建安全管理器 SecurityManager工厂
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro-first.ini");
        //创建安全管理器SecurityManager
        SecurityManager securityManager = factory.getInstance();
        //将SecurityManager设置到当前运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //从SecurityUtils里面创建一个主体
        Subject subject =  SecurityUtils.getSubject();
        //创建认证使用的token
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","1111111");
        //subject提交认证
        try{
            subject.login(token);
        }catch (Exception e){
            e.printStackTrace();
        }
        //是否认证通过
        boolean isAuthenticate =  subject.isAuthenticated();
        System.out.println("通过认证:"+isAuthenticate);
        System.out.println("是否认证通过:" + isAuthenticated);	
        //退出操作
        subject.logout();	
        //是否认证通过
        isAuthenticated =  subject.isAuthenticated();	
        System.out.println("是否认证通过:" + isAuthenticated);	
    }

(3)执行流程分析
  1、通过ini配置文件创建securityManager
  2、调用subject.login方法主体提交认证,提交的token
  3、securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证。
  4、ModularRealmAuthenticator调用IniRealm(给realm传入token) 去ini配置文件中查询用户信息
  5、IniRealm根据输入的token(UsernamePasswordToken)从 shiro-first.ini查询用户信息,根据账号查询用户信息(账号和密码)
  如果查询到用户信息,就给ModularRealmAuthenticator返回用户信息(账号和密码)
  如果查询不到,就给ModularRealmAuthenticator返回null
  6、ModularRealmAuthenticator接收IniRealm返回Authentication认证信息
  如果返回的认证信息是null,ModularRealmAuthenticator抛出异常(org.apache.shiro.authc.UnknownAccountException)
  如果返回的认证信息不是null(说明inirealm找到了用户),对IniRealm返回用户密码 (在ini文件中存在)和 token中的密码 进行对比,如果不一致抛出异常(org.apache.shiro.authc.IncorrectCredentialsException)
(4)总结
  ModularRealmAuthenticator作用进行认证,需要调用realm查询用户信息(在数据库中存在用户信息)
ModularRealmAuthenticator进行密码对比(认证过程)。

  realm:需要根据token中的身份信息去查询数据库(入门程序使用ini配置文件),如果查到用户返回认证信息,如果查询不到返回null
3、自定义realm
(1)定义realm要实现AuthorizingRealm接口

public class CustomerRealm extends AuthorizingRealm {
    @Override
    public void setName(String name) {
        super.setName(name);
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
       //从token中取出身份信息
        String userCode = (String) authenticationToken.getPrincipal();
        //从数据库中去查询该用户是否存在
        //如果查询不到就返回 null
        //如果查询到了,就获取该用户的密码
        String password = "111111";
        //返回认证信息
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userCode,password,this.getName());
        return simpleAuthenticationInfo;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
}

(2)需要在shiro-realm.ini配置realm注入到securityManager中。

[main]
#自定义realm
customRealm=com.jack.realm.CustomerRealm
#将realm设置到securityManager
securityManager.realms=$customRealm

(3)测试代码

 public void test2(){
        //创建安全管理器 SecurityManager工厂
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro-realm.ini");
        //创建安全管理器SecurityManager
        SecurityManager securityManager = factory.getInstance();
        //将SecurityManager设置到当前运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //从SecurityUtils里面创建一个主体
        Subject subject =  SecurityUtils.getSubject();
        //创建认证使用的token
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","111111");
        //subject提交认证
        try{
            subject.login(token);
        }catch (Exception e){
            e.printStackTrace();
        }
        //是否认证通过
        boolean isAuthenticate =  subject.isAuthenticated();
        System.out.println("通过认证:"+isAuthenticate);
    }

4、散列算法
(1)介绍
 通常需要对密码 进行散列,常用的有md5、sha,

 对md5密码,如果知道散列后的值可以通过穷举算法,得到md5密码对应的明文。
建议对md5进行散列时加salt(盐),进行加密相当 于对原始密码+盐进行散列。
正常使用时散列方法:
在程序中对原始密码+盐进行散列,将散列值存储到数据库中,并且还要将盐也要存储在数据库中。

 如果进行密码对比时,使用相同 方法,将原始密码+盐进行散列,进行比对。
(2)md5散列测试程序

//md5加密,不加盐
		String password_md5 = new Md5Hash("111111").toString();
		System.out.println("md5加密,不加盐="+password_md5);
		
		//md5加密,加盐,一次散列
		String password_md5_sale_1 = new Md5Hash("111111", "eteokues", 1).toString();
		System.out.println("password_md5_sale_1="+password_md5_sale_1);
		String password_md5_sale_2 = new Md5Hash("111111", "uiwueylm", 1).toString();
		System.out.println("password_md5_sale_2="+password_md5_sale_2);
		//两次散列相当于md5(md5())

		//使用SimpleHash
		String simpleHash = new SimpleHash("MD5", "111111", "eteokues",1).toString();
		System.out.println(simpleHash);

(3)自定义realm支持散列算法

//继承AuthorizingRealm类,实现自定义realm
public class CustomerRealmMd5 extends AuthorizingRealm {
    @Override
    public void setName(String name) {
        super.setName(name);
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
       //从token中取出身份信息
        String userCode = (String) authenticationToken.getPrincipal();
        //从数据库中去查询该用户是否存在
        //如果查询不到就返回 null
        //如果查询到了,就获取该用户的密码
        String password = "745c144d7721368a3257dcd0f728e4f1";
        //从数据库获取salt信息
        String salt = "adfgx";
        //返回认证信息
        SimpleAuthenticationInfo simpleAuthenticationInfo = new
                SimpleAuthenticationInfo(userCode,password, ByteSource.Util.bytes(salt),this.getName());
        return simpleAuthenticationInfo;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
}

(4)定义散列的凭证匹配器

[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1

#将凭证匹配器设置到realm
customRealm=com.jack.realm.CustomerRealmMd5
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm
posted @ 2017-08-28 21:50  一条路上的咸鱼  阅读(933)  评论(0编辑  收藏  举报