身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。
关键对象
访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体;
是主体(subject)进行身份认证的标识,标识必须具有唯一性
,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)。
是只有主体自己知道的安全信息,如密码、证书等。
认证流程

认证的开发
1. 创建项目并引入依赖
1 2 3 4 5
| <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.5.3</version> </dependency>
|
2. 引入shiro配置文件并加入如下配置

1 2 3 4
| [users] kylin=123 zhangsan=123456 lisi=789
|
3.开发认证代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public class TestAuthenticator { public static void main(String[] args) { DefaultSecurityManager securityManager = new DefaultSecurityManager(); securityManager.setRealm(new IniRealm("classpath:shiro.ini")); SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("kylin", "123");
System.out.println("认证状态" + subject.isAuthenticated()); subject.login(token); System.out.println("认证状态" + subject.isAuthenticated()); } }
|

UnknownAccountException
(未知账户异常,用户名错误)
IncorrectCredentialsException
(不正确的凭证异常,密码错误)
DisabledAccountException(帐号被禁用)
LockedAccountException(帐号被锁定)
ExcessiveAttemptsException(登录失败次数过多)
ExpiredCredentialsException(凭证过期)等
自定义Realm
上边的程序使用的是Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。
1.shiro提供的Realm

2.根据认证源码认证使用的是SimpleAccountRealm

SimpleAccountRealm的部分源码中有两个方法一个是 认证 一个是 授权
SimpleAccountRealm
—->doGetAuthenticationInfo
用户名校验
AuthenticatingRealm
—->assertCredentialsMatch
密码校验
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
| public class SimpleAccountRealm extends AuthorizingRealm { protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken) token; SimpleAccount account = 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 = getUsername(principals); USERS_LOCK.readLock().lock(); try { return this.users.get(username); } finally { USERS_LOCK.readLock().unlock(); } } }
|

所以密码的验证是shiro自动调用assertCredentialsMatch
来验证的,我们自用将doGetAuthenticationInfo
中获取的认证信息改成数据库中获取即可。继承AuthorizingRealm
类,就可以从数据库中进行认证。

SimpleAccountRealm
—->继承AuthorizingRealm
—->继承AuthenticatingRealm
- 自定义认证继承
AuthenticatingRealm
实现doGetAuthenticationInfo
方法
- 自定义授权继承
AuthorizingRealm
实现doGetAuthorizationInfo
方法

3.自定义realm
自定义认证继承AuthenticatingRealm
实现doGetAuthenticationInfo
方法(继承AuthorizingRealm实现也行,因为AuthorizingRealm继承了AuthenticatingRealm)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public class CustomerRealm extends AuthenticatingRealm {
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { Object principal = authenticationToken.getPrincipal(); System.out.println(principal); if ("kylin".equals(principal)) { SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal, "123", this.getName()); return simpleAuthenticationInfo; } return null; } }
|
Object principal = authenticationToken.getPrincipal();
在token中获取用户名
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal, "123", this.getName());
new SimpleAuthenticationInfo(Object principal, Object credentials, String realmName)
- 参数1:返回数据库正确的用户名
- 参数2:返回数据库中正确密码
- 参数3:提供当前realm的名字
this.getName();
通过SimpleAuthenticationInfo
构造了以下认证信息
4.使用自定义Realm认证

使用自定Realm进行认证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public class TestCustomerRealmAuthenticator { public static void main(String[] args) {
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(new CustomerRealm()); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("kylin", "123");
System.out.println("认证状态" + subject.isAuthenticated()); subject.login(token); System.out.println("认证状态" + subject.isAuthenticated());
} }
|
此时UsernamePasswordToken
和我们在自定义中构造的认证信息一致。所以认证成功!

以后我们就能在doGetAuthenticationInfo
中根据principal
用户名再数据库中查找数据,构建成SimpleAuthenticationInfo
就能进行认证!