文章目录
一、认证
身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。
二、 shiro中认证的关键对象
1、subject:主体
访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体;
2、Principal:身份信息
是主体(subject)进行身份认证的标识,标识必须具有
唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)。
3、credential:凭证信息
是只有主体自己知道的安全信息,如密码、证书等。
三、认证流程
四、认证的开发
1、创建maven项目并引入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
2、引入shiro配置文件并加入如下配置
[users]
zhangsan=123
lisi=456
czy=789
3、开发认证代码
// 测试shiro的身份认证
public class TestAuthenticator {
public static void main(String[] args) {
// 1.创建shiro的安全管理器
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 2.给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
// 3.SecurityUtils 给全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
// 4.使用SecurityUtils获取用户的身份信息subject主体
Subject subject = SecurityUtils.getSubject();
// 5.创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan1", "1233");
try {
System.out.println("身份认证:" + subject.isAuthenticated());
// 6.进行身份认证
subject.login(token);
System.out.println("身份认证:" + subject.isAuthenticated());
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名不存在!");
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误!");
}
}
}
五、自定义Realm
上边的程序使用的是Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义Realm。
1、shiro提供的Realm
2、根据认证源码认证使用的是SimpleAccountRealm
SimpleAccountRealm的部分源码中有两个方法一个是认证 一个是授权
public class SimpleAccountRealm extends AuthorizingRealm {
//......
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken)token;
SimpleAccount account = this.getUser(upToken.getUsername());
if (account != null) {
if (account.isLocked()) {
throw new LockedAccountException("Account [" + account + "] is locked.");
}
if (account.isCredentialsExpired()) {
String msg = "The credentials for account [" + account + "] are expired";
throw new ExpiredCredentialsException(msg);
}
}
return account;
}
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = this.getUsername(principals);
this.USERS_LOCK.readLock().lock();
AuthorizationInfo var3;
try {
var3 = (AuthorizationInfo)this.users.get(username);
} finally {
this.USERS_LOCK.readLock().unlock();
}
return var3;
}
}
3、自定义Realm
/**
* 自定义realm
*/
public class CustomRealm extends AuthorizingRealm {
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 通过token获取用户输入的信息
String principal = (String) token.getPrincipal();
System.out.println(principal);
// 通过用户名来查询数据库中的信息 jdbc jpa mybatis
if ("zhangsan".equals(principal)){
// 参数1:返回数据库中正确的用户名 参数2:返回数据库正确的密码 参数3:获取当前realm的名字
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal, "123", this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}
4、使用自定义Realm认证
/**
* 测试使用自定义的realm来实现shiro身份认证
*/
public class TestCustomRealmAuthenticator {
public static void main(String[] args) {
// 1.创建安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
// 2.设置自定义的realm(相当于从数据库中读取的)
defaultSecurityManager.setRealm(new CustomRealm());
// 3.使用安全工具类设置管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
// 4.使用安全工具类获取subject主体
Subject subject = SecurityUtils.getSubject();
// 5.使用用户名和密码创建一个令牌token(相当于用户自己输入的用户名和密码信息)
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123123");
try {
// 6.使用subject进行登录
subject.login(token);
System.out.println(subject.isAuthenticated());
} catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名错误!");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误!");
}
}
}
六、使用MD5和Salt
实际应用是将盐和散列后的值存在数据库中,Realm从数据库取出盐和加密后的值由shiro完成密码校验。
MD5加密算法的特点:算法不可逆(只能由明文加密得到密文,反之则不行);如果内容相同,无论进行多少次MD5加密其结果始终保持一致。
MD5算法的应用:加密; 签名
1、自定义md5+salt的Realm
/**
* 自定义一个带有MD5 + salt + 散列的realm
*/
public class CustomMD5Realm extends AuthorizingRealm {
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
// 身份认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 从token中获取身份信息
String principal = (String) token.getPrincipal();
// 根据用户名到数据库中进行查找
if ("zhangsan".equals(principal)){
return new SimpleAuthenticationInfo(principal,
"4090ec9c2cfd8341455ae0e6c80ae696",
ByteSource.Util.bytes("QPX@!"),
this.getName());
}
return null;
}
}
2、使用md5 + salt 认证
**
* 测试MD5加密算法身份认证
*/
public class TestCustomMD5RealmAuthenticator {
public static void main(String[] args) {
// 1.创建安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
// 2.设置自定义realm
CustomMD5Realm customMD5Realm = new CustomMD5Realm();
// 使用hash凭证匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 使用的加密算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 设置哈希散列的次数
hashedCredentialsMatcher.setHashIterations(1024);
customMD5Realm.setCredentialsMatcher(hashedCredentialsMatcher);
defaultSecurityManager.setRealm(customMD5Realm);
// 3.注入安全工具类
SecurityUtils.setSecurityManager(defaultSecurityManager);
// 4.获取subject主体
Subject subject = SecurityUtils.getSubject();
// 5.获取token
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
// 6.登录认证
subject.login(token);
System.out.println("登录成功");
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误!");
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误!");
}
}
}