第二章 身份验证(学习笔记)
principals:身份,如(用户名,邮箱,手机号) 唯一即可
credentials:证明/凭证 如密码 ,安全证书
1.1 环境准备
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency> </dependencies>
1.2 登录/退出
1. 先准备一些用户身份
[users]
zhanglisheng=123
zhangchouqian=1234
2. 测试用例:com.zls.shiro.Login
@Test public void testLogin(){ //1.创建SecurityManager 工厂 Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //2.得到SecurityManager实例 org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); //3.得到Subject 创建用户名密码 身份验证Token Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("zhang","123"); //4.身份验证 subject.login(token); //用户是否登录 Assert.assertEquals(true, subject.isAuthenticated()); //5.退出 subject.logout(); }
2.1 首先通过 new IniSecurityManagerFactory 指定ini 文件创建一个SecurityManager工厂
2.2 然后获取 SecurityManager 绑定到 SecurityUtils(全局变量,绑定一次即可)
2.3 通过SecurityUtils 获取Subject, 然后获取身份验证的Token
2.4 subject.login 进行登录 会自动委托给 securityManager.login
2.5 如果身份验证失败请捕获AuthenticationException或其子类,常见的如: DisabledAccountException(禁用的帐号)、LockedAccountException(锁定的帐号)、UnknownAccountException(错误的帐号)、ExcessiveAttemptsException(登录失败次数过多)、IncorrectCredentialsException (错误的凭证)、ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;对于页面的错误消息展示,最好使用如“用户名/密码错误”而不是“用户名错误”/“密码错误”,防止一些恶意用户非法扫描帐号库
2.6 最后 subject.logout 退出, 自动委托给 securityManager.logout
1.3 身份认证流程
1.首先通过 subject.login 进行登录 ,之前必须设置 SecurityUtils.setSecurityManager(securityManager)
2.Security Manager 负责身份验证的业务逻辑,他会委托给Authenticator (验证器) 进行验证
3. Authenticator 身份验证,此处可以插入自己的实现
4. Authenticator 可能会委托给相应的 AuthenticationStrategy (认证策略) 进行多Realm身份验证,默认
ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行验证
5. Authenticator 会把相应的Token传入Realm,从Realm中获取身份验证信息。 如果没有返回/抛出异常,代码
验证失败(可以配置多个Realm,按照配置策略循序访问)
1.4 Realm
之前咱们已经说过, Shiro会从Realm中获取安全数据(用户,角色,权限)
1. 创建一个类 ShiroRealm
public class ShiroRealm implements Realm{ @Override public String getName() { return "myRealm"; } @Override public boolean supports(AuthenticationToken token) { //仅支持UsernamePasswordToken类型的Token return token instanceof UsernamePasswordToken; } @Override public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken) token; //获取用户名 String username = upToken.getUsername(); String password = String.valueOf(upToken.getPassword()); if(!"zhang".equals(username)) { throw new UnknownAccountException(); //如果用户名错误 } if(!"123".equals(password)) { throw new IncorrectCredentialsException(); //如果密码错误 } //如果身份认证验证成功,返回一个AuthenticationInfo实现; return new SimpleAuthenticationInfo(username, password, getName()); } }
2. 创建 shiro-realm.ini
#声明一个realm myRealm=com.zls.shiro.realm.ShiroRealm #指定securityManager的realms实现 securityManager.realms=$myRealm
多Realm配置
#声明一个realm myRealm=com.zls.shiro.realm.ShiroRealm myRealm2=com.zls.shiro.realm.ShiroRealm2 #指定securityManager的realms实现 securityManager.realms=$myRealm,$myRealm2
按照指定的顺序执行身份验证,如果不指定realms 就按照声明的顺序执行
如图 : 一般继承 AuthorizingRealm(授权) 即可,因为AuthorizingRealm继承了AuthenticatingRealm(身份验证)也间接继承了CachingRealm(缓存)
JDBC Realm使用
1.数据库的依赖
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.25</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>0.2.23</version> </dependency>
2.数据库表结构
3. ini配置(shiro-jdbc-realm.ini)
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm dataSource=com.alibaba.druid.pool.DruidDataSource dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql://192.168.50.24:3306/test dataSource.username=root dataSource.password=ytyk@2017 jdbcRealm.dataSource=$dataSource securityManager.realms=$jdbcRealm
1.5 Authenticator (验证器)及 AuthenticationStrategy(认证策略)
@Override public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
如果验证成功返回 AuthenticationInfo ,失败抛出 AuthenticationException异常
SecurityManager接口继承了Authenticator,另外还有一个ModularRealmAuthenticator实现,其委托给多个Realm进行验证,验证规则通过AuthenticationStrategy接口指定,默认提供的实现:
FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回一个验证成功的认证信息
AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,返回所有验证成功信息
AllSuccessfulStrategy:所有验证成功才算成功,返回所有验证成功信息
ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy策略。
创建shiro-authenticator-all-success.ini
#指定securityManager的authenticator实现 authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator securityManager.authenticator=$authenticator #指定securityManager.authenticator的authenticationStrategy allSuccessfulStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy #声明一个realm myRealm=com.zls.shiro.realm.ShiroRealm myRealm2=com.zls.shiro.realm.ShiroRealm2 #指定securityManager的realms实现 securityManager.realms=$myRealm,$myRealm2
此时,某一个realm验证失败,就会验证失败