Shiro之Realm认证授权介绍(三)

验证的理论过程

1、先获取当前的subject

//获取当前的subject
Subject currentUser = SecurityUtils.getSubject();

2、校验当前的subject是否已经被认证

currentUser.isAuthenticated()

3、若没有被认证
把用户名和密码封装为UsernamePasswordToken对象

  • 创建一个表单页面

  • 将请求提交到spring mvc的handler对象

  • 获取用户名和密码

4、执行登录。执行subject的login(AuthenticationToken)方法

5、自定义Realm方法,从数据库中获取对应的记录,返回给shiro

  • 实际上需要继承org.apache.shiro.realm.AuthorizingRealm类

  • 实现doGetAuthenticationInfo(AuthenticationToken authcToken)方法

6、由shiro完成对密码的比对

补充:

1)密码的比对是通过AuthorizingRealm的 credetialsMatcher属性来进行的密码比对

2)加密对比:

1、如何将一个字符串密码进行加密成MD5

2、替换当前Realm的credentialsMatcher属性,直接使用HashedCredentialsMatcher进行加密设置

操作实现

1、创建一个login.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>登录页面</title>
</head>
<body>
	<h1>login</h1>
	<form action="shiro/login" method="post">
		username:<input  type = "text" name = "username"/><br/><br/>
		password:<input  type = "text" name = "password"/><br/><br/>
		<input type="submit" value = "submit">
	</form>
</body>
</html>

2、创建一个handler(类似controller)

package com.example.handlers;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/shiro")
public class ShiroHandler {
    
    
	/**
	 * 登录方法,登录成功返回success页面
	 * 
	 * @param username:用户名
	 * @param password:密码
	 * @return
	 */
	@RequestMapping("/login")
	public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
    
    
		Subject currentUser = SecurityUtils.getSubject();
		// 测试当前的用户是否登录,也就是是否被认证
		if (!currentUser.isAuthenticated()) {
    
    
			// 把用户名和密码封装为UsernamePasswordToken对象
			UsernamePasswordToken token = new UsernamePasswordToken(username, password);
			// RememberMe
			token.setRememberMe(true);
			try {
    
    
				// 执行登录
				currentUser.login(token);
			}
			// 认证的所有异常处理
			catch (AuthenticationException ae) {
    
    
				System.out.println("登录失败:"+ae.toString());
			}
		}
		return "redirect:/success.jsp";
	}
}

3、创建一个Realm,来实现登录(要在applicationContext-shiro中进行配置)
继承AuthenticatingRealm接口(若只实现认证)

继承AuthorizingRealm接口(实现认证和授权)

public class ShiroRealm extends AuthorizingRealm {
    
    

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
    
    
		return null;
	}
	//认证回调函数 
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
		System.out.println("doGetAuthenticationInfo:"+token);
		//1、把AuthenticationToken强转为UsernamePasswordToken
		UsernamePasswordToken t = (UsernamePasswordToken) token;
		//2、从t中获取用户名和密码
		String username = t.getUsername();
		//3、调用数据库的方法,从数据库查询对应的用户记录
		System.out.println("从数据库中取数据:"+username+"的用户信息为");
		//3.1小试一下就是来静态数据
		
		//4、若用户名不存在,抛出异常
		if (username.equals("unknown")) {
    
    
			System.out.println("登录失败,用户不存在");
			throw new UnknownAccountException("用户不存在");
		} 
		//5、根据用户信息的情况,看是否抛出其他异常
		if (username.equals("status_disable")) {
    
    
			System.out.println("登录失败,用户被禁用");
			throw new LockedAccountException("用户被禁用");
		} 
		//6、根据用户的情况,来构建AuthenticationToken对象并返回
		//6.1以下信息是从数据库中获取的
		//principals:认证的实体信息,可以是数据库中的实体类对象,可以是username
		Object principals = username;
		//credentials:从数据库获取的密码
		Object credentials = "123456";
		//realmName:当前realm对象的name,直接调用父类的getName()方法即可
		String realmName = getName();
		SimpleAuthenticationInfo simInfo = new SimpleAuthenticationInfo(principals, credentials,realmName);
		return simInfo;
	}

}

4、拦截设置(登录请求被拦截了)(在applicationContext-shiro配置中设置的)

<property name="filterChainDefinitions">
	<value>
		/login.jsp = anon
		/shiro/login = anon
        /shiro/loginout = logout
		/** = authc
	</value>
</property>

补充:(关于Realm的操作,对加密密码进行对比)
1、如何将一个字符串密码进行加密成MD5
2、替换当前Realm的credentialsMatcher属性,直接使用HashedCredentialsMatcher进行加密设置
在applicationContext-shiro.xml中配置Realm的时候进行配置

<!-- 3、配置realm,新建一个ShiroRealm类,实现Realm接口 -->
	<bean id="jdbcRealm" class="com.example.realms.ShiroRealm" >
		<property name="credentialsMatcher">
			<bean class = "org.apache.shiro.authc.credential.HashedCredentialsMatcher">
				<!-- 加密的算法 -->
				<property name="hashAlgorithmName" value="MD5"></property>
				<!-- 加密的次数 -->
				<property name="hashIterations" value="1024"></property>
			</bean>
		</property>
	
	</bean>

得知一个密码加密后的密码:

public static void main(String[] args) {
    
    
		String hashAlgorithmName = "MD5";
		Object credentials = "123456";
		Object salt = null;
		int hashIterations = 1024;
		System.out.println("加密前的密码:"+credentials);
		SimpleHash hash = new SimpleHash(hashAlgorithmName,credentials,salt,hashIterations);
		System.out.println("加密后的密码:"+hash);
	}

利用salt来实现盐值加密(两个用户密码相同,加密后的到的密码不同)

ByteSource.Util.bytes(username):一般使用唯一的那一个属性来进行加盐

//6、根据用户的情况,来构建AuthenticationToken对象并返回
//6.1以下信息是从数据库中获取的
//principals:认证的实体信息,可以是数据库中的实体类对象,可以是username
Object principals = username;
//credentials:从数据库获取的密码
Object credentials = "123456";
//realmName:当前realm对象的name,直接调用父类的getName()方法即可
String realmName = getName();
//salt来实现盐值加密,若username唯一,可以使用username来加盐
ByteSource credentialsSalt = ByteSource.Util.bytes(username)
SimpleAuthenticationInfo simInfo = new SimpleAuthenticationInfo(principals, credentials,credentialsSalt,realmName);
return simInfo;

多Realm

1、建立两个Realm(ShiroRealm与LoginRealm)

2、配置Realm(在applicationContext-shiro.xml文件中配置)

执行顺序会与配置文件中list中配置realm的顺序相同

	<bean id="securityManager"
		class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="cacheManager" ref="cacheManager" />
		<property name="sessionMode" value="native" />
		<property name="authenticator" ref="modularRealmAuthenticator" />
	</bean>
 
     <!-- 认证器 -->
	<bean id="modularRealmAuthenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
		<property name="realms">
			<list>
				<ref bean="shiroRealm"/>
				<ref bean="loginRealm"/>
			</list>
		</property>
	</bean>


<!-- 3、配置realm,新建一个ShiroRealm类,实现Realm接口 -->
	<bean id="shiroRealm" class="com.example.realms.ShiroRealm" >
		<property name="credentialsMatcher">
			<bean class = "org.apache.shiro.authc.credential.HashedCredentialsMatcher">
				<!-- 加密的算法 -->
				<property name="hashAlgorithmName" value="MD5"></property>
				<!-- 加密的次数 -->
				<property name="hashIterations" value="1024"></property>
			</bean>
		</property>
	</bean>
	
	<bean id="loginRealm" class="com.example.realms.LoginRealm" >
		<property name="credentialsMatcher">
			<bean class = "org.apache.shiro.authc.credential.HashedCredentialsMatcher">
				<!-- 加密的算法 -->
				<property name="hashAlgorithmName" value="SHA1"></property>
				<!-- 加密的次数 -->
				<property name="hashIterations" value="1024"></property>
			</bean>
		</property>
	</bean>

认证策略 Authentication Strategy

Authentication Strategy接口的默认实现

  • FirstSuccessfulStrategy:只要有一个Ream验证成功即可,只返回笫一个Ream身份验证成功的认证信息,其他的忽略;

  • AtLeastoneSuccessfulStrategy:只要有一个Ream验证成功即可,和FirstSuccessful Strategy不同,将返回所有 Realm身份验证成功的认证信息

  • AllSuccessfulStrategy:所有Ream验证或功才算成功,且返回所有Ream身份验证成功的认证信息,如果有个失败就失败了。

默认是 AtLeastone SuccessfulStrateagy策略

<!-- 认证器 -->
<bean id="modularRealmAuthenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
	<property name="realms">
		<list>
			<ref bean="shiroRealm"/>
			<ref bean="loginRealm"/>
		</list>
	</property>
<!--该属性来修改认证策略-->
	<property name="authenticationStrategy" >
		<bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"></bean>
	</property>
</bean>

另一种方法就是全部写好,自己转换

<bean id="modularRealmAuthenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
    	<property name="authenticationStrategy" ref="firstSuccessfulStrategy"></property>
	</bean>
	<bean id="atLeastOneSuccessfulStrategy" class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean><!-- 多realm策略:只要有一个(或更多)的Realm验证成功,那么认证将被视为成功 -->
	<bean id="firstSuccessfulStrategy" class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"></bean><!-- 多realm策略:第一个Realm验证成功,整体认证将被视为成功,且后续Realm将被忽略 -->
	<bean id="allSuccessfulStrategy" class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean><!-- 多realm策略:所有Realm成功,认证才视为成功 -->

授权

授权,也叫访问控制,即在应用中控制谁访问哪些资源〔如访问页面/编辑数据/面操作,在授权中需了解的几个关键对象:主体( Subject)、资源( Resource)、权限( Permission)、角色(Role)

  • 主体( Subject):访问应用的用户,在 Shiro中使用 Subject代表该用户。用户只有授权后才允许访问相应的资源。

  • 资源( Resource):在应用中用户可以访问的URL,比如访问JSP页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

  • 权限( Permission):安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改倗删除用户数据〔即很多时候都是CRUD(增查改删)式权限控制)等。权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允限〔如用户模块的所有权限)和细粒度权限

  • 角色(Role):一般情况下会赋予用户角色而不是权限,即这样用户可以拥有组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。
    在这里插入图片描述

在securityManager与modularRealmAuthenticator都可以进行Realm的配置
若想进行授权,需要在securityManager中配置而不是在modularRealmAuthenticator中配置

<bean id="securityManager"
	class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="cacheManager" ref="cacheManager" />
		<property name="sessionMode" value="native" />
		<property name="authenticator" ref="modularRealmAuthenticator" />
		<property name="realms">
			<list>
				<ref bean="shiroRealm"/>
				<ref bean="loginRealm"/>
			</list>
		</property>
</bean>

授权需要继承AuthorizingRealm类,同时实现认证和授权

//用于授权的方法
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
    
    
		//1、从principal获取用户登录信息
		Object primaryPrincipal = principal.getPrimaryPrincipal();
		//2、利用登录的用户信息来获取当前用户的信息和权限
		Set<String> roles = new HashSet<String>();
		roles.add("user");
		if(primaryPrincipal.equals("admin")) {
    
    
			roles.add("a");
		}
		//3、利用SimpleAuthorizationInfo,并设置roles属性
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
		//4、返回SimpleAuthorizationInfo对象
		return info;
	}


public class ShiroRealm extends AuthorizingRealm {
    
    
	//用于授权的方法
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
    
    
		//1、从principal获取用户登录信息
		Object primaryPrincipal = principal.getPrimaryPrincipal();
		//2、利用登录的用户信息来获取当前用户的信息和权限
		Set<String> roles = new HashSet<String>();
		roles.add("user");
		if(primaryPrincipal.equals("admin")) {
    
    
			roles.add("a");
		}
		//3、利用SimpleAuthorizationInfo,并设置roles属性
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
		//4、返回SimpleAuthorizationInfo对象
		return info;
	}
	//认证回调函数 
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
		System.out.println("第二个Relam:ShiroRealm");
		//1、把AuthenticationToken强转为UsernamePasswordToken
		UsernamePasswordToken t = (UsernamePasswordToken) token;
		//2、从t中获取用户名和密码
		String username = t.getUsername();
		//3、调用数据库的方法,从数据库查询对应的用户记录
		System.out.println("从数据库中取数据:"+username+"的用户信息为");
		//3.1小试一下就是来静态数据
		
		//4、若用户名不存在,抛出异常
		if (username.equals("unknown")) {
    
    
			System.out.println("登录失败,用户不存在");
			throw new UnknownAccountException("用户不存在");
		} 
		//5、根据用户信息的情况,看是否抛出其他异常
		if (username.equals("status_disable")) {
    
    
			System.out.println("登录失败,用户被禁用");
			throw new LockedAccountException("用户被禁用");
		} 
		//6、根据用户的情况,来构建AuthenticationToken对象并返回
		//6.1以下信息是从数据库中获取的
		//principals:认证的实体信息,可以是数据库中的实体类对象,可以是username
		Object principals = username;
		//credentials:从数据库获取的密码
		Object credentials = "123456";
		//realmName:当前realm对象的name,直接调用父类的getName()方法即可
		String realmName = getName();
		SimpleAuthenticationInfo simInfo = new SimpleAuthenticationInfo(principals, credentials,realmName);
		return simInfo;
	}
	public static void main(String[] args) {
    
    
		String hashAlgorithmName = "MD5";
		Object credentials = "123456";
		Object salt = null;
		int hashIterations = 1024;
		System.out.println("加密前的密码:"+credentials);
		SimpleHash hash = new SimpleHash(hashAlgorithmName,credentials,salt,hashIterations);
		System.out.println("加密后的密码:"+hash);
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_42272869/article/details/113137666