shiro中自定义Realm的实现

  1. 最终执行用户名比较 SimpleAccountRealm doGetAuthenticationInfo 在这个方法中完成用户名校验

  2. 最终密码校验是在AuthenticatingRealm中 assertCredentialsMatch

总结:

  • AuthenticatingRealm 认证realm doGetAuthenticationInfo

  • AuthorizingRealm 授权realm doGetAuthorizationInfo

自定义Realm的目的就是为了抛弃掉ini配置文件

实现与数据库和以后Spring的连接

创建一个realm包用于存放我们自定义的CustomerRealm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.ceit.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
* 自定义realm实现 将认证/授权数据的来源转为数据库
*/

public class CustomerRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//在token中获取用户名
String principal = (String)authenticationToken.getPrincipal();
System.out.println(principal);
//根据身份信息使用JDBC Mybatis查询相关数据库
if ("xiaochen".equals(principal)){
//参数1:数据库中查询到的用户名 //参数2:返回数据库中正确密码 //参数3:提供当前realm的名字 this.getName();
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,"123",this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}

写一个类来测试一下
TestCustomerRealmAuthenticator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.ceit;

import com.ceit.realm.CustomerRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;

//使用自定义realm
public class TestCustomerRealmAuthenticator {
public static void main(String[] args) {
//创建securityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//设置自定义realm
defaultSecurityManager.setRealm(new CustomerRealm());
//将安全工具类设置安全工具类
SecurityUtils.setSecurityManager(defaultSecurityManager);
//通过安全工具类获取subject
Subject subject = SecurityUtils.getSubject();
//创建token
UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");

try {
subject.login(token);
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误");
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误");
}

}
}

这样自定义的realm就写好了 以后只需要将CustomerRealm中

1
2
3
4
5
6
//根据身份信息使用JDBC Mybatis查询相关数据库
if ("xiaochen".equals(principal)){
//参数1:数据库中查询到的用户名 //参数2:返回数据库中正确密码 //参数3:提供当前realm的名字 this.getName();
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,"123",this.getName());
return simpleAuthenticationInfo;
}

修改为JDBC或Mybatis即可

MD5和Salt的简介和执行流程说明

刚才虽然完成了自定义realm的实现,但是还有一个问题就是,认证用的密码还在是明文存储的

一般开发中都是加密和加盐的 避免安全隐患(sql注入)

  • MD5算法:

    • 作用:一般用来加密 或者 签名(检验和)

    • 特点:MD5算法不可逆 如果内容相同无论执行多少次md5生成结果始终是一致的

    • 生成结果:始终是一个16进制 32位长度字符串

MD5+Salt实现

先来测试一下md5的用法
TestShiroMD5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.ceit;

import org.apache.shiro.crypto.hash.Md5Hash;

public class TestShiroMD5 {
public static void main(String[] args) {
//创建一个md5算法
// Md5Hash md5Hash = new Md5Hash();
// md5Hash.setBytes("123".getBytes());
// String s = md5Hash.toHex();
// System.out.println(s);

//使用md5
Md5Hash md5Hash = new Md5Hash("123");
System.out.println(md5Hash.toHex());

//使用md5+salt处理
Md5Hash md5Hash1 = new Md5Hash("123","X0*7ps");
System.out.println(md5Hash1.toHex());

//使用md5 + salt + hash散列
Md5Hash md5Hash2 = new Md5Hash("123", "X0*7ps", 1024);
System.out.println(md5Hash2);

}
}

运行结果
1
2
3
202cb962ac59075b964b07152d234b70
8a83592a02263bfe6752b2b5b03a4799
e4f9bf3e0c58f045e62c23c533fcf633

创建一个CustomerMd5Realm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.ceit.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**
* 使用自定义realm 加入md5 + salt +hash
*/
public class CustomerMd5Realm extends AuthorizingRealm {

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

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取身份信息
String principal = (String)authenticationToken.getPrincipal();

// if("xiaochen".equals(principal)){
// return new SimpleAuthenticationInfo(principal,
// "202cb962ac59075b964b07152d234b70",
// this.getName());
// }

//参数1 数据库用户名 参数2 数据库md5+salt之后的密码 参数3 注册时的随机盐 参数4 realm的名字
// if("xiaochen".equals(principal)){
// return new SimpleAuthenticationInfo(principal,
// "8a83592a02263bfe6752b2b5b03a4799",
// ByteSource.Util.bytes("X0*7ps"),
// this.getName());
// }

if("xiaochen".equals(principal)){
return new SimpleAuthenticationInfo(principal,
"e4f9bf3e0c58f045e62c23c533fcf633",
ByteSource.Util.bytes("X0*7ps"),
this.getName());
}
return null;
}
}

这里就直接用md5 + salt + hash来演示了

再创建TestCustomerMd5RealmAuthenticator类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.ceit;

import com.ceit.realm.CustomerMd5Realm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;

public class TestCustomerMd5RealmAuthenticator {
public static void main(String[] args) {
//创建安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//注入realm
CustomerMd5Realm realm = new CustomerMd5Realm();
//设置realm使用hash凭证匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//使用算法
credentialsMatcher.setHashAlgorithmName("md5");
//散列次数
credentialsMatcher.setHashIterations(1024);
realm.setCredentialsMatcher(credentialsMatcher);

defaultSecurityManager.setRealm(realm);
//通过安全工具类获取subject
SecurityUtils.setSecurityManager(defaultSecurityManager);
//通过安全工具类获取subject
Subject subject = SecurityUtils.getSubject();
//创建token
UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");

try {
subject.login(token);
System.out.println("登录成功");
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误");
}
}
}

运行结果
1
登录成功

这样一个md5+salt+hash的认证就完成了

到这里shiro的认证就基本完成了