谈谈Shiro的原理及在SSM和SpringBoot两种环境下的使用姿式(上篇)


title: 谈谈Shiro的原理及在SSM和SpringBoot两种环境下的使用姿式(上篇) categories:git

  • Spring tags:
  • Shiro使用

本篇主要是记录关于Shiro进行认证和受权的大体原理,而后是单独在Shiro中实现认证和受权的方式。最后主要说明在传统SSM的工程中使用Shiro和在SpringBoot的工程中使用Shiro进行整合。关于认证和受权,我这里采用的是规范的RBAC权限模型,数据库的建表语句已经托管github的工程中。github

在进行Shiro具体认证和受权的流程介绍以前,首先说一下Shiro中几个比较重要的概念(其中的接口或者类)。算法

  1. Subject:含义为主体。Subject做为用户端(使用Shiro进行受权的一端)的抽象,在Shiro中是经过一个接口来体现的。在使用Shiro的时候,咱们也就是经过调用Subject的认证和受权的方法来实现具体的认证和受权的。sql

  2. SecurityManager:含义为安全管理器。SecurityManager是整个Shiro的核心所在,它负责对全部的Subject进行安全管理,咱们在经过Subject进行受权和认证的时候,Subject实际上是经过SecurityManager来实现具体业务逻辑的。SecurityManager在Shiro中是经过一个接口来体现的,并且它继承了Authenticator,Authorizer,SessionManager。以下图:数据库

    Aaron Swartz
    这样SecurityManager的认证会交给Authenticator定义的业务逻辑完成,受权会交给Authorizer定义的业务逻辑完成,会话管理会交给SessionManager来完成。

  3. Authenticator:含义为认证器,主要是完成对用户身份的认证。Authenticator在Shiro是一个接口,在Shiro中提供了一个ModularRealmAuthenticator的实现类用于完成认证。ModularRealmAuthenticator已经能够完成大多数的认证需求,若是咱们有新的业务,那么咱们经过自定义认证器来完成特殊的业务。apache

  4. Authorizer:含义为受权器,主要是完成对用户操做的受权。Authorizer在Shiro中是一个接口,相比Authenticator,Shiro提供了更多的受权器的实现类,其中也包括相似的ModularRealmAuthorizer。这些默认的实现类能够完成大多数需求,若是咱们有新的业务,那么咱们经过自定义受权器来完成特殊的业务。缓存

  5. realm:含义为领域。Realm在Shiro中也是一个接口,SecurityManager进行认证和受权的时候,它所须要的数据信息都是从Realm中获取的。Realm有多种实现类,表明了Realm能够从多种数据源中读取已经配置的数据信息用于认证和受权。Realm在Shiro中是一个至关关键的部分,由于咱们的受权器和认证器最终都是经过Realm来实现各自的业务的。安全

  6. SessionDAO:含义为Session会话.在Shiro中也是做为一个接口来体现的。sessiondao能够实现将session数据持久化。session

  7. CacheManager:含义为缓存管理器。用户的认证和受权的信息能够缓存到CacheManager中,从而提高数据的访问性能。app

  8. Cryptography:含义为密码管理。shiro提供了Cryptography来做为咱们信息的加密和界面的工具。

ok,在说完了Shiro中几个比较关键的概念以后,咱们开始看一下在Shiro中是如何进行的认证和受权的。

注:全部的sql都包含在了工程中

代码地址: github.com/fuyunwang/S… 认证:

下面这张图说明了Shiro中进行认证的大体流程。

Aaron Swartz

能够看到,Shiro最终其实经过Realm来完成最终的认证。咱们上面也已经提到,Realm其实做为一种数据源的地位存在,其包含多个实现类表明着从不一样的数据源中进行数据信息的获取。我这里经过使用其中一个实现类IniRealm来实现最简单的认证流程。(具体代码在v0.1tag下。)

Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:inirealm-shiro.ini");
	
	SecurityManager securityManager = factory.getInstance();
	
	SecurityUtils.setSecurityManager(securityManager);
	
	Subject subject = SecurityUtils.getSubject();
	
	UsernamePasswordToken token=new UsernamePasswordToken("beautifulsoup", "password");
	
	try{
		subject.login(token);
	}catch(AuthenticationException e){
		e.printStackTrace();
	}
复制代码

介绍完使用inirealm来完成从ini配置文件中获取数据以后,咱们作一个自定义Realm,来完成从数据库这一数据源中获取数据。 自定义Realm通常采用继承自AuthorizingRealm的方式,而后重写其中的认证和受权的方法,核心代码以下,完整代码在github。

@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		String username=(String) token.getPrincipal();
		ShiroDemoMapper mapper=getShiroMapper();
		User user = mapper.findByUsername(username);
		if(null!=user){
			SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(username, user.getPassword(),TAG);
			return authenticationInfo;
		}
		return null;
	}
复制代码

而后进行配置:

[main]
	#进行自定义realm的配置
	customRealm=com.beautifulsoup.shiro.demo.realm.ShiroDemoRealm
	securityManager.realms=$customRealm
复制代码

这样咱们自定义的realm就会生效了,咱们能够实现从数据库中获取数据,而后校验咱们主体subject的信息,从而实现判断是否定证成功的功能。 这里咱们认证的时候,在数据库中采用明文存取的密码,这固然是不合理的,因此一般状况下,咱们会采用加盐(salt)的方式,使用散列算法如MD5对咱们原有的密码进行加密而后存入数据库中。(改进以后的代码在v0.2标签下)

首先修改配置文件,定义散列算法和散列次数等:
	[main]
	credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
	credentialsMatcher.hashAlgorithmName=md5
	credentialsMatcher.hashIterations=3
	#进行自定义realm的配置
	customRealm=com.beautifulsoup.shiro.demo.realm.ShiroDemoRealm
	customRealm.credentialsMatcher=$credentialsMatcher
	securityManager.realms=$customRealm
	而后修改realm
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		String username=(String) token.getPrincipal();
		ShiroDemoMapper mapper=getShiroMapper();
		User user = mapper.findByUsername(username);
		if(null!=user){
			SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(username, user.getPassword(),
					ByteSource.Util.bytes(user.getSalt()),TAG);
			return authenticationInfo;
		}
		return null;
	}
复制代码

好,说完认证咱们接下来讲受权:

受权:

一样,下面这张图说明了在Shiro中进行受权的大体流程。

Aaron Swartz

能够看到,SecurityManager最终交给Realm进行受权,实际上Realm是会返回一个ModularRealmAuthorizer类,该类获得全部的系统配置的权限而后调用PermissionResolver进行了权限的匹配。

接上所讲,咱们仍是使用ini的配置文件来配置shiro实现受权,主要是配置文件更加方便咱们的管理。

这里咱们的权限信息定义在配置文件中,毕竟咱们的权限信息大多数是固定的,并且对于权限很少的状况下,这种方式更简单。对于受权的操做主要包括针对角色的受权和针对资源的受权两种方式,因为基于角色的权限控制不如基于资源的权限控制更加灵活,因此咱们采用基于资源的权限控制为例来介绍。

配置文件进行配置的方式以下(代码在v0.3标签):

[users]
		#用户beautifulsoup具备role1和role3的角色
		beautifulsoup=password,role1,role3
		[roles]
		#权限role1具备对01用户订单的建立权限和对02订单资源的修改权限和对全部订单的查询操做。
		role1=item:create:01,item:update:02,item:query
		role2=item:*:01,item:update:02
		role3=item:create:02,item:delete:02
复制代码

基本权限的验证:

@Test
		public void testIniAuthorization(){
			Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:permission-shiro.ini");
			SecurityManager securityManager = factory.getInstance();
			SecurityUtils.setSecurityManager(securityManager);
			Subject subject = SecurityUtils.getSubject();
			//首先认证,认证经过以后才能受权
			UsernamePasswordToken token=new UsernamePasswordToken("beautifulsoup", "password");
			try{
				subject.login(token);
			}catch(AuthenticationException e){
				e.printStackTrace();
			}
			System.out.println("用户的认证状态:"+subject.isAuthenticated());
			boolean isPermitted=subject.isPermittedAll("item:create:01","item:query");
			subject.checkPermissions("item:create:01","item:query");
			System.out.println(isPermitted);
		}
复制代码

接下来使用自定义realm来实现用户的受权。 在认证中已经提到继承自AuthorizingRealm,其提供了两个方法,咱们如今用第二个方法来实现受权的逻辑。(代码在v0.4标签)

@Override
		protected AuthorizationInfo doGetAuthorizationInfo(
				PrincipalCollection principals) {
			//获得认证成功以后凭证的身份信息
			String username=(String) principals.getPrimaryPrincipal();
			//查询数据库获得全部的权限列表
			List<String> permissionList=new ArrayList<String>();
			UserCustomMapper mapper=getUserCustomMapper();
			UserCustom userCustom = mapper.findUserCustomByUsername(username);
			Set<RoleCustom> roles=userCustom.getRoleSet();
			for(RoleCustom role:roles){
				Set<Permission> permissionSet = role.getPermissionSet();
				for (Permission permission:permissionSet) {
					permissionList.add(permission.getPname());
				}
			}
			SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
			authorizationInfo.addStringPermissions(permissionList);
			return authorizationInfo;
		}		
		一样咱们也须要配置:
		[main]
		credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
		credentialsMatcher.hashAlgorithmName=md5
		credentialsMatcher.hashIterations=3
		#进行自定义realm的配置
		customRealm=com.beautifulsoup.shiro.demo.realm.ShiroDemoRealm
		customRealm.credentialsMatcher=$credentialsMatcher
		securityManager.realms=$customRealm
复制代码

OK,到如今为止上篇已经对Shiro全部认证和受权的基础知识作过了介绍,下篇开始对SSM和SpringBoot中的Shiro的使用进行整合。 代码地址: github.com/fuyunwang/S…

若是对您有过帮助,感谢您的一个star。

相关文章
相关标签/搜索