1、用户须要提供principals (身份)和credentials(证实)给shiro,从而应用能验证用户身份。java
principals:身份,即主体的标识属性,能够是任何东西,如用户名、邮箱等,惟一便可。一个主体能够有多个principals,但只有一个Primary principals,通常是用户名/密码/手机号。
credentials:证实/凭证,即只有主体知道的安全值,如密码/数字证书等。mysql
最多见的principals和credentials组合就是用户名/密码。web
测试代码:sql
/** * @Project: testshiro * @Title: TestShiro.java * @Package com.yuan.shiro.test * @author yuan * @date 2016年7月4日 下午1:28:28 * @Copyright: 2016 * @version V1.0 */ package com.yuan.shiro.test; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.junit.Test; /** * @ClassName TestShiro * @author yuan * @version 1.0 */ public class TestShiro { @Test public void testLogin(){ //1.获取SecurityManager工厂,此处使用ini配置文件初始化SecurityManager工厂 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:com/yuan/shiro/test/shiro.ini"); //2.获得SecurityManager实例,并绑定到SecurityUtils SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); //3.经过SecurityUtils获得Subject获得Subject,其会自动绑定到当前线程,若是在web环境在请求结束时须要解除绑定 //而后获取身份验证的Token,如用户名/密码; Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("u1","1234"); try { //4.登陆进行身份验证,其会自动委托给SecurityManager.login方法进行登陆 //此处与ini中配置文件进行比对 subject.login(token); System.out.println("登陆成功"); } catch (AuthenticationException e) { //5.验证失败 //常见的如:DisabledAccountException(禁用的账号)、LockedAccountException(锁定的账号)、UnknownAccountException(错误的账号) //ExcessiveAttemptsException(登陆失败次数过多)、IncorrectCredentialsException (错误的凭证)、ExpiredCredentialsException(过时的凭证) System.out.println("登陆失败"); e.printStackTrace(); } //判断用户是否已登陆 if( subject.isAuthenticated() ){ System.out.println("已登陆"); }else{ System.out.println("未登陆"); } //6.退出,自动委托给SecurityManager.logout方法退出。 subject.logout(); } }
shiro.ini数据库
[users] u1=123 u2=123
2、身份验证的步骤:apache
一、收集用户身份/凭证,即如用户名/密码;
二、调用Subject.login 进行登陆,若是失败将获得相应的AuthenticationException 异常,根据异常提示用户错误信息;不然登陆成功;api
3、身份认证的流程:缓存
一、首先调用Subject.login(token)进行登陆,其会自动委托给Security Manager,调用以前必须经过SecurityUtils. setSecurityManager()设置;
二、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
三、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处能够自定义插入本身的实现;
四、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
五、Authenticator 会把相应的token 传入Realm,从Realm 获取身份验证信息,若是没有返回/抛出异常表示身份验证失败了。此处能够配置多个Realm,将按照相应的顺序及策略进
行访问。安全
4、realmide
Realm:域,Shiro 从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它须要从Realm获取相应的用户进行比较以肯定用户身份是否合法;也须要从Realm获得用户相应的角色/权限进行验证用户是否能进行操做;能够把Realm当作DataSource , 即安全数据源。
org.apache.shiro.realm.Realm接口有三个方法
String getName(); //返回一个惟一的Realm名字 boolean supports(AuthenticationToken token); //判断此Realm是否支持此Token AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException; //根据Token获取认证信息
a、单Realm配置
一、自定义Realm实现
/** * @Project: testshiro * @Title: TestRealm1.java * @Package com.yuan.shiro.realm * @author yuan * @date 2016年7月4日 下午3:14:41 * @Copyright: 2016 * @version V1.0 */ package com.yuan.shiro.realm; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.realm.Realm; /** * @ClassName TestRealm1 * @author yuan * @version 1.0 */ public class TestRealm1 implements Realm{ /** * <p>Title: getAuthenticationInfo</p> * <p>Description: 根据Token获取认证信息</p> * @param token * @return * @throws AuthenticationException * @see org.apache.shiro.realm.Realm#getAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) */ @Override public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String userName = (String)token.getPrincipal(); //获得用户名 String pwd = new String((char[])token.getCredentials()); //获得密码 if(!"u1".equals(userName)) { throw new UnknownAccountException(); //若是用户名错误 } if(!"123".equals(pwd)) { throw new IncorrectCredentialsException(); //若是密码错误 } //若是身份认证验证成功,返回一个AuthenticationInfo实现; return new SimpleAuthenticationInfo(userName, pwd, this.getName()); } /** * <p>Title: getName</p> * <p>Description:返回一个惟一的Realm名字 </p> * @return * @see org.apache.shiro.realm.Realm#getName() */ @Override public String getName() { return "TestRealm1"; } /** * <p>Title: supports</p> * <p>Description: 判断此Realm是否支持此Token</p> * @param token * @return * @see org.apache.shiro.realm.Realm#supports(org.apache.shiro.authc.AuthenticationToken) */ @Override public boolean supports(AuthenticationToken token) { //仅支持UsernamePasswordToken 类型的Token return token instanceof UsernamePasswordToken; } }
二、ini配置文件指定自定义Realm实现
[main] #声明一个realm testRealm1=com.yuan.shiro.realm.TestRealm1 #指定securityManager的realms实现 securityManager.realms=$testRealm1
经过$name 来引入自定义的realm
b、多Realm配置
[main] #声明一个realm testRealm1=com.yuan.shiro.realm.TestRealm1 testRealm2=com.yuan.shiro.realm.TestRealm2 #指定securityManager的realms实现 securityManager.realms=$testRealm1,$testRealm2
securityManager会按照realms指定的顺序进行身份认证,若是删除“securityManager.realms=$myRealm1,$myRealm2”,那么securityManager会按照realm声明的顺序进行使用。
c、shiro默认的Realm
通常继承AuthorizingRealm(受权)便可;其继承了AuthenticatingRealm(即身份验证),也间接继承了CachingRealm(带有缓存实现)。其中主要默认实现以下:
org.apache.shiro.realm.text.IniRealm:[users]部分指定用户名/密码及其角色;[roles]部分指定角色即权限信息;
org.apache.shiro.realm.text.PropertiesRealm:user.username=password,role1,role2指定用户名/密码及其角色;role.role1=permission1,permission2指定角色及权限信息;
org.apache.shiro.realm.jdbc.JdbcRealm:经过sql查询相应的信息,如“select password fromusers where username = ?”获取用户密码,“select password, password_salt from users whereusername = ?”获取用户密码及盐;“select role_name from user_roles where username = ?”获取用户角色;“select permission from roles_permissions where role_name = ?”获取角色对应的权限信息;也能够调用相应的api进行自定义sql;
d、jdbcRealm
一、数据库下建表,shiro默认表
create table users ( id bigint auto_increment, username varchar(100), password varchar(100), password_salt varchar(100), constraint pk_users primary key(id) ) charset=utf8 ENGINE=InnoDB; create unique index idx_users_username on users(username); create table user_roles( id bigint auto_increment, username varchar(100), role_name varchar(100), constraint pk_user_roles primary key(id) ) charset=utf8 ENGINE=InnoDB; create unique index idx_user_roles on user_roles(username, role_name); create table roles_permissions( id bigint auto_increment, role_name varchar(100), permission varchar(100), constraint pk_roles_permissions primary key(id) ) charset=utf8 ENGINE=InnoDB; create unique index idx_roles_permissions on roles_permissions(role_name, permission); insert into users(username,password)values('u1','123');
二、ini配置
[main] jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm dataSource=org.apache.commons.dbcp.BasicDataSource dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql://localhost:3306/online dataSource.username=root dataSource.password=admin jdbcRealm.dataSource=$dataSource securityManager.realms=$jdbcRealm
变量名=全限定类名会自动建立一个类实例
变量名.属性=值自动调用相应的setter方法进行赋值
$变量名引用以前的一个对象实例
三、测试
5、Authenticator
Authenticator的职责是验证用户账号,是Shiro API中身份验证核心的入口点:
public AuthenticationInfo authenticate(AuthenticationToken authenticationToken) throws AuthenticationException;
若是验证成功,将返回AuthenticationInfo 验证信息;此信息中包含了身份及凭证;若是验证失败将抛出相应的AuthenticationException实现。
SecurityManager接口继承了Authenticator,另外还有一个ModularRealmAuthenticator实现,其委托给多个Realm 进行验证,验证规则经过AuthenticationStrategy 接口指定,默认提供
的实现:
FirstSuccessfulStrategy:只要有一个Realm验证成功便可,只返回第一个Realm身份验证成功的认证信息,其余的忽略;
AtLeastOneSuccessfulStrategy:至少有一个Realm验证成功便可,和FirstSuccessfulStrategy不一样,返回全部Realm身份验证成功的认证信息;
AllSuccessfulStrategy:全部Realm验证成功才算成功,且返回全部Realm身份验证成功的认证信息,若是有一个失败就失败了。
ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy策略。
假设咱们有两个realm:
testRealm1: 用户名/密码为u1/123时成功,且返回身份/凭据为u1/123;
testRealm2: 用户名/密码为u1/123 时成功,且返回身份/凭据为u1@1.com/123,
和testRealm1不一样的是返回时的身份变了;
一、ini配置文件
[main] #指定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 testRealm1=com.yuan.shiro.realm.TestRealm1 testRealm2=com.yuan.shiro.realm.TestRealm2 securityManager.realms=$testRealm1,$testRealm2
测试:
/** * @Project: testshiro * @Title: TestAuthenticator.java * @Package com.yuan.shiro.test * @author yuan * @date 2016年7月4日 下午4:01:30 * @Copyright: 2016 * @version V1.0 */ package com.yuan.shiro.test; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.junit.Test; /** * @ClassName TestAuthenticator * @author yuan * @version 1.0 */ public class TestAuthenticator { @Test public void testAllSuccessfulStrategyWithSuccess() { login("classpath:com/yuan/shiro/test/shiro.ini"); Subject subject = SecurityUtils.getSubject(); // 获得一个身份集合,其包含了Realm验证成功的身份信息 PrincipalCollection principalCollection = subject.getPrincipals(); System.out.println("个数====>"+principalCollection.asList().size()); } /** * 登陆 * * @MethodName login * @author yuan * @date 2016年7月4日 下午4:02:08 * @return void */ private void login(String config) { // 一、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory( config); // 二、获得SecurityManager实例 并绑定给SecurityUtils org.apache.shiro.mgt.SecurityManager securityManager = factory .getInstance(); SecurityUtils.setSecurityManager(securityManager); // 三、获得Subject及建立用户名/密码身份验证Token(即用户身份/凭证) Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("u1", "123"); try { subject.login(token); } catch (AuthenticationException e) { System.out.println("登陆失败"); e.printStackTrace(); } } }