您的位置:首页 > 其它

Shiro学习笔记——(4)身份认证

2018-04-01 00:19 405 查看

一、环境项目搭建

(1)Maven依赖

<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

(2)配置log4j.properties日志文件

log4j.rootLogger=debug, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

(3)创建shiro.ini文件

即 用户身份/凭据通过Shiro.ini配置文件初始化SecurityManager环境。
#对用户信息进行配置
[users]
#用户名密码
zhang=123
lisi=123
配置 eclipse支持ini文件编辑:



二、用户认证

(1)代码实现

com.shiro.authentication.AuthenticationTest.javapublic class AuthenticationTest {

/**
* 登录和登出
*/
@Test
public void testLoginAndLogOut() {
// 构建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:shiro.ini");
// 通过工厂创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 将securityManager设置当前的运行环境中
SecurityUtils.setSecurityManager(securityManager);

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

// 在认证提交前准备token(令牌)
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
// 执行认证提交
subject.login(token);
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

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

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

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

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

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

(2)执行流程 

前置代码流程
首先通过new IniSecurityManagerFactory并指定一个ini配置文件来创建一个SecurityManager工厂;
接着获取SecurityManager并绑定到SecurityUtils,这是一个全局设置,设置一次即可;
通过SecurityUtils得到Subject,其会自动绑定到当前线程;如果在web环境在请求结束时需要解除绑定;然后获取身份验证的Token,如用户名/密码;
认证流程:
通过ini配置文件创建securityManager
创建token令牌,token中有用户提交的认证信息即账号和密码

调用subject.login方法主体提交认证,提交的token
securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证。
Authenticator的实现ModularRealmAuthenticato调用IniRealm(给realm传入token) 去ini配置文件中查询用户信息
IniRealm根据输入的token(UsernamePasswordToken)从shiro.ini查询用户信息,根据账号查询用户信息(账号和密码)。查询到用户信息,给ModularRealmAuthenticator返回用户信息(账号和密码)。如果查询不到,就给ModularRealmAuthenticator返回null。
ModularRealmAuthenticator接收IniRealm返回Authentication认证信息。ModularRealmAuthenticator对IniRealm返回用户密码 (在ini文件中存在)和 token中的密码 进行对比。
如果身份验证失败捕获AuthenticationException或其子类,常见的如: DisabledAccountException(禁用的帐号)、LockedAccountException(锁定的帐号)、UnknownAccountException(错误的帐号)、ExcessiveAttemptsException(登录失败次数过多)、IncorrectCredentialsException (错误的凭证)、ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;

三、自定义Realm

(1)Realm介绍

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

(2)Realm接口架构



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

(3)自定义Realm代码

public class MyRealm extends AuthorizingRealm {

@Override
public String getName() {
return "myrealm1";
}
/**
* 用于认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
// 从token中 获取用户身份信息
String username = (String) token.getPrincipal();
// 拿username从数据库中查询
// ....
// 如果查询不到则返回null
if (!username.equals("zhang")) {// 这里模拟查询不到

return null;
}

// 获取从数据库查询出来的用户密码
String password = "123";// 这里使用静态数据模拟。。

// 返回认证信息由父类AuthenticatingRealm进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,
password, this.getName());

return simpleAuthenticationInfo;

}
}
在shiro中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能验证用户身份:principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。最常见的principals和credentials组合就是用户名/密码了。接下来先进行一个基本的身份认证。

(4)配置Realm

ini配置文件指定自定义Realm实现(shiro-realm.ini)  #声明一个realm
myRealm1=com.github.zhangkaitao.shiro.chapter2.realm.MyRealm1
#指定securityManager的realms实现
securityManager.realms=$myRealm1 通过$name来引入之前的realm定义,相当于spring中的注入

(5)测试

同上边的入门程序,需要更改ini配置文件路径:Factory<SecurityManager>factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");

四、Realm支持MD5加密

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

(1)MD5本地测试

public class MD5Test {

public static void main(String[] args) {
/**
* Md5Hash
*/
// 要加密的字符串
String source = "123";
// 盐
String salt = "1";
// 加密次数
int hashIterations = 1024;
Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations);
System.out.println(md5Hash.toString());

/**
* SimpleHash
*/
// 使用的算法名称
String algorithmName = "MD5";
SimpleHash simpleHash = new SimpleHash(algorithmName, source, salt, hashIterations);
System.out.println(simpleHash.toString());
}
}
6932058980060f2c9130b59fd53b063b
6932058980060f2c9130b59fd53b063b

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

1、Realm代码

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
// 从token中 获取用户身份信息
String username = (String) token.getPrincipal();
// 拿username从数据库中查询
// 如果查询不到则返回null
if (!username.equals("zhang")) {// 这里模拟查询不到 return
return null;
}
// 获取从数据库查询出来的用户密码123
String password = "6932058980060f2c9130b59fd53b063b";
// 从数据库查询出来的盐
String salt = "1";
// 返回认证信息由父类AuthenticatingRealm进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,
password, ByteSource.Util.bytes(salt), this.getName());
return simpleAuthenticationInfo;

}
2、在realm中配置凭证匹配器
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1024

#将凭证匹配器设置到realm
customRealm=com.shiro.realm.MD5Realm
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Shiro