Shiro的学习(二)——Shiro认证

一、环境

使用maven进行管理,pom.xml文件:

<dependencies>
		<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>

		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.3</version>
		</dependency>

		<!-- 单元测试 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.2</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.2</version>
		</dependency>

	</dependencies>

也可以直接:

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-all</artifactId>
	<version>1.2.3</version>
</dependency>

通过Shiro.ini配置文件初始化SecurityManager环境。

为了方便测试将用户名和密码配置在shiro.ini配置文件中:

[users]
zhang=123
lisi=123

二、测试用例:

/**
	 * 用户登录退出
	 */
	@Test
	public void test1() {

		// 建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境
		IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
		// 通过工厂创建SecurityManager
		SecurityManager securityManager = factory.getInstance();
		// 将securityManager设置到运行环境中
		SecurityUtils.setSecurityManager(securityManager);
		// 创建一个Subject实例,该实例认证要使用上边创建的securityManager进行
		Subject subject = SecurityUtils.getSubject();
		// 得到token令牌,记录用户认证的身份和凭证即账号和密码
		UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
		try {
			// 用户登陆
			subject.login(token);
		} catch (AuthenticationException e) {
			e.printStackTrace();
		}
		// 用户认证状态
		Boolean isAuthenticated = subject.isAuthenticated();
		System.out.println("用户认证状态:" + isAuthenticated);

		// 用户退出
		subject.logout();
		isAuthenticated = subject.isAuthenticated();
		System.out.println("用户认证状态:" + isAuthenticated);
	}

 执行流程

Ⅰ首先通过new IniSecurityManagerFactory并指定一个ini配置文件来创建一个SecurityManager工厂; 

Ⅱ接着获取SecurityManager并绑定到SecurityUtils,这是一个全局设置,设置一次即可;

Ⅲ通过SecurityUtils得到Subject,其会自动绑定到当前线程;如果在web环境在请求结束时需要解除绑定;然后获取身份验证的Token,如用户名/密码

Ⅳ调用subject.login方法进行登录,其会自动委托给SecurityManager.login方法进行登录;

Ⅴ如果身份验证失败请捕获AuthenticationException或其子类,常见的如: DisabledAccountException(禁用的帐号)、LockedAccountException(锁定的帐号)、UnknownAccountException(错误的帐号)、ExcessiveAttemptsException(登录失败次数过多)、IncorrectCredentialsException (错误的凭证)、ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;对于页面的错误消息展示,最好使用如“用户名/密码错误”而不是“用户名错误”/“密码错误”,防止一些恶意用户非法扫描帐号库;

Ⅵ最后可以调用subject.logout退出,其会自动委托给SecurityManager.logout方法退出。

小结:

从如上代码可总结出身份验证的步骤:

1、收集用户身份/凭证,即如用户名/密码;

2、调用Subject.login进行登录,如果失败将得到相应的AuthenticationException异常,根据异常提示用户错误信息;否则登录成功;

3、最后调用Subject.logout进行退出操作。

如上测试的几个问题:

1、用户名/密码硬编码在ini配置文件,以后需要改成如数据库存储,且密码需要加密存储;

2、用户身份Token可能不仅仅是用户名/密码,也可能还有其他的,如登录时允许用户名/邮箱/手机号同时登录

认证流程:

流程如下:

1、首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;

2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;

3、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;

4、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;

5、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。 此处目前使用配置文件代替,后期改为数据库!

三、Realm

Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。如我们之前的ini配置方式将使用org.apache.shiro.realm.text.IniRealm。

最基础的是Realm接口,CachingRealm负责缓存处理,AuthenticationRealm负责认证,AuthorizingRealm负责授权,通常自定义的realm继承AuthorizingRealm。

自定义Realm

public class CustomRealm1 extends AuthorizingRealm {

	// 返回一个唯一的Realm名字
	@Override
	public String getName() {
		return "customRealm1";
	}

	// 根据Token获取认证信息
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String username = (String) token.getPrincipal(); // 得到用户名
		
		String password = new String((char[]) token.getCredentials()); // 得到密码
		
		if (!"zhang".equals(username)) { //模拟从数据库查询 没有这个账号
			throw new UnknownAccountException(); // 如果用户名错误
		}
		if (!"123".equals(password)) { //模拟查询出来的的密码不是 123
			throw new IncorrectCredentialsException(); // 如果密码错误
		}
		// 如果身份认证验证成功,返回一个AuthenticationInfo实现;
		return new SimpleAuthenticationInfo(username, password, getName());
	}

	// 用于授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		return null;
	}
}

猜你喜欢

转载自blog.csdn.net/qq_41061437/article/details/84779099