Shrio认证详解+自定义Realm
Authentication(身份认证)是Shiro权限控制的第一步,用来告诉系统你就是你。
在提交认证的时候,我们需要给系统提交两个信息:
Principals:是一个表示用户的唯一属性,可以是用户名,邮箱之类的。
Credentials:是证明用户身份的证书,可以是密码或者指纹之类的。
认证主要分为三步:
1、收集认证信息
2、提交认证信息
3、如果认证成功,则允许访问,否则就拒绝访问或者重试。
收集认证信息:
在入门的例子中,使用了一个UsernamePasswordToken来收集用户的用户名和密码,用来登陆。
这个类支持最简单的用户名和密码登陆。实现了org.apache.shiro.authc.AuthenticationToken接口。
AuthenticationToken接口是认证系统的基础,只有getCredentials(),getPrincipal()两个方法用来获取基本的认证信息。
HostAuthenticationToken和RememberMeAuthenticationToken是它的两个子接口。
HostAuthenticationToken只有一个getHost()方法用来获取请求的地址信息。
RememberMeAuthenticationToken只有一个isRememberMe()方法用来标记用户是否需要记住我。
然后还有两个子类UsernamePasswordToken和CasToken提供了基本的实现。其中CasToken已经被废弃了。
例:
//Example using most common scenario of username/password pair:
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//"Remember Me" built-in:
token.setRememberMe(true);
提交认证信息:
收集好认证信息之后,保存为AuthenticationToken的一个实例,我们需要提交这个认证信息来进行认证。
进行认证前,我们首先需要获取当前用户(Subject)。然后调用login方法来进行登陆。
例:
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
处理结果:
如果登陆成功的话,则不会有什么异常,此时如果调用isAuthenticated()方法,则会返回true。
如果登陆失败的话,则被抛出异常,在SHiro中,提供了很多异常类,可以用来捕捉具体的异常。
例子:
try {
currentUser.login(token);
} catch ( UnknownAccountException uae ) { ...
} catch ( IncorrectCredentialsException ice ) { ...
} catch ( LockedAccountException lae ) { ...
} catch ( ExcessiveAttemptsException eae ) { ...
} ... catch your own ...
} catch ( AuthenticationException ae ) {
//unexpected error?
} //No problems, continue on as expected...
可以看到,前面出现了Remembered和Authenticated。那这两种有什么区别呢?
需要注意的是,这是两种互斥的情况,当我们在登陆的时候,登陆成功之后,我们Authenticated返回的是true。
选择Remeber me的时候,我们下次可以不登陆直接访问,而我们下次登陆之后,Remembered返回的是true,但是Authenticated返回的是false。
在登陆之后,我们可以可以用logout()方法来注销。这时候用户的信息都会被清空,包括保存在Cookie中的RemeberMe信息还有session也会被无效。
上面我们就简单的讲述了一下在代码中实现登陆验证的流程。具体实例可以参考入门的例子。
那么,在Shrio内部是怎么样的一个认证流程呢?大概可以用下图来概括:
第一步:在代码中调用login方法,传递构造好的AuthenticationToken实例。
第二步:通过一个DelegatingSubject来分发认证请求给SecurityManager
第三步:SecurityManager容器会把接收到的token简单的转发给它内部的认证器实例通过调用认证器的authenticate(token)方法。
这通常是一个ModularRealmAuthenticator实例,用来支持多个Realm。
第四步:如果有定义多个Realm则ModularRealmAuthenticator会初始化一个支持多个Realm的认证器,通过配置的AuthenticationStrategy。
第五步:每一个配置的Realm都会被检测是否支持提交的AuthenticationToken,如果支持的话就会调用getAuthenticationInfo方法,从Realm中获取数据来跟提交的token进行验证。
验证器
在SecurityManager中,默认使用ModularRealmAuthenticator实例,它不仅仅支持单Realm还支持多个Realm。
如果实在单个Realm的情况下,ModualrRealmAuthenticator会直接调用这个Realm来尝试验证。
如果我们需要定义自己的验证器的话,可以通过在配置文件的[main]中如下定义:
[main]
...
authenticator = com.foo.bar.CustomAuthenticator
securityManager.authenticator = $authenticator
验证策略
如果是只有一个Realms的时候,则不需要验证策略。
如果是有两个以上的Realm的时候,ModularRealmAuthenticator依赖于AuthenticationStrategy组件来决定认证成功或者失败的条件,
比如是一个成功就成功还是要都成功才是成功之类的。。。。
在Shiro里面已经有三个认证策略的实现
AtLeastOneSuccessfulStrategy:只要有一个或一个以上的认证成功就表示认证成功
FirstSuccessfulStrategy;只有第一个认证成功的时候才表示认证成功
AllSuccessfulStrategy:只有所有的都认证成功的时候才表示认证成功。
在ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy的验证策略。当然也可以通过下面的方式定义其他的验证策略。
[main]
...
authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authcStrategy
...
Realm认证顺序
在定义了多个Realm的时候,如果有多个Realm支持当前的AuthenticationToken,则会依次调用Realm的getAuthenticationInfo方法。
调用的顺序分为两种:
隐式的:
如果在SecurityManager中定义了如下的几个Realm,则会按照他们定义的顺序去调用。
blahRealm = com.company.blah.Realm
...
fooRealm = com.company.foo.Realm
...
barRealm = com.company.another.Realm
此时的效果就等于下面的语句。
securityManager.realms = $blahRealm, $fooRealm, $barRealm、
显示的:
就如上面的一样,如果在securityManager.realms中配置的时候,改变realm的配置顺序,则会按照这个配置顺序来调用,这就是显示的配置。
blahRealm = com.company.blah.Realm
...
fooRealm = com.company.foo.Realm
...
barRealm = com.company.another.Realm
securityManager.realms = $fooRealm, $barRealm, $blahRealm
...
域认证
前面说了下Realm的认证策略和认证顺序,那么,在Realm认证的时候,究竟发生什么事情了呢?
supporting AuthenticationTokens
在Realm被用来尝试认证登陆的时候首先会调用supports方法,来检测是否能解析这个AuthenticationToken,决定是否进行认证。
Handing supported AuthenticationTokens
如果这个realm支持提交的AuthenticationTokens的话,解析器会调用Realm的getAuthenticationINfo(token)方法,来尝试认证,
这个方法大概做了下面这些事情:
1、检测token中的唯一用户标识
2、基于唯一标识 去数据源中查找
3、确保提供的证书跟数据源中保存的一样
4、如果证书一样,就封装一个AuthenticationINfo实例返回
5、如果证书不匹配,则抛出一个AuthenticationException异常
下面,我们来看看Shrio提供的Realm的类结构:
org.apache.shiro.realm.Realm(I):base
org.apache.shiro.realm.CachingRealm(Abstract):提供缓存支持
org.apache.shiro.realm.AuthenticatingRealm(Abstract):提供认证支持
org.apache.shiro.realm.AuthorizingRealm(Abstract):提供授权支持
org.apache.shiro.realm.SimpleAccountRealm(C):简单的用户名密码支持
org.apache.shiro.realm.text.TextConfigurationRealm(C):支持Text文件的简单用户名密码支持
org.apache.shiro.realm.text.IniRealm(C):通过INI文件的简单用户名密码支持(默认使用这个)
org.apache.shiro.realm.text.PropertiesRealm(C):支持属性文件的简单用户名密码支持
org.apache.shiro.realm.jdbc.JdbcRealm(C):支持通过JDBC认证
在系统默认的情况下,系统使用的是IniRealm这个实现,可以从INI配置文件的[users]和[roles]两个节中读取用户信息和权限信息。
如果我们需要定义自己的Realm实现的话,一般都是继承AuthorizingRealm。
稍候,我们将简单介绍下如果从db中来实现认证。
证书匹配:
前面我们说过,Realm需要去匹配用户提交的证书跟数据源中存储的证书是否匹配,如果匹配的话就认为是认证成功。
在获得用户唯一标识后,系统回去Realm会去检索用户的证书,然后通过CredentialsMatcher来检测证书是否匹配。
Shrio中也提供了一些证书的匹配器可以直接拿来使用,使用下面的方法变更默认的匹配器:
[main]
...
customMatcher = com.company.shiro.realm.CustomCredentialsMatcher
myRealm = com.company.shiro.realm.MyRealm
myRealm.credentialsMatcher = $customMatcher
...
Simple Equality Check
默认情况下所有提供的Realm实现都是使用SimpleCredentialsMatcher来检测证书是否匹配。
不过一般情况下都不需要变更,因为默认的就足够了。
Hashing Credentials
相比使用原始的数据存储起来,拿来匹配,我们更愿意将证书加密进行存储来进行匹配。那么如何使用呢?
在Shiro中提供了几个HashedCredentialsMatcher的子类,用来实现这个功能。包括MD5、SHA-256等等的加密方式。
我们可以通过下面的配置方式:
[main]
...
credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
# base64 encoding, not hex in this example:
credentialsMatcher.storedCredentialsHexEncoded = false
credentialsMatcher.hashIterations = 1024
# This next property is only needed in Shiro 1.0\. Remove it in 1.1 and later:
credentialsMatcher.hashSalted = true ...
myRealm = com.company.....
myRealm.credentialsMatcher = $credentialsMatcher
...
需要注意的是,这种情况下在Realm的实现中需要返回一个SaltedAuthenticationInfo,而不是普通的AuthenticationInfo,因为在用户提交认证的时候,需要获取相同的salt来进行加密,进行匹配认证。
那这个salt(盐)是用来干嘛的呢?
这是因为在进行MD5之类加密的时候,还是可以进行破解的,但是如果加入一个变量来进行加密之后,就基本上是无法破解了(不知道这个SALT的情况下)。
下面我们就做一个通过JDBC来认证的登陆认证程序。点此看源码
首先我们需要一个用户表,脚本如下:
CREATE DATABASE `db_shiro` USE `db_shiro`; DROP TABLE IF EXISTS `users`; CREATE TABLE `users` (
`id` int(4) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; insert into `users`(`id`,`username`,`password`) values (1,'fuwh','');
我们需要定义一个shiro_jdbc.ini文件如下
[main]
dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
dataSource.driverClass=com.mysql.jdbc.Driver
dataSource.jdbcUrl=jdbc:mysql://localhost:3306/db_shiro
dataSource.user=root
dataSource.password=rootadmin ;this is comment read from mysql
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSource
securityManager.realms=$jdbcRealm
编写登陆认证代码:
package com.fuwh.demo; import org.apache.shiro.SecurityUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory; public class ShiroDemo02 { private static Logger log=LoggerFactory.getLogger(ShiroDemo02.class);
public static void main(String[] args) {
//取得SecurityManager工厂
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro_jdbc.ini");
//取得SecurityManager实例
SecurityManager securityManager=factory.getInstance();
//将securityManager绑定到SecurityUtil
SecurityUtils.setSecurityManager(securityManager); /* 至此为止,简单的从mysql数据库读取realm信息的shiro环境就配置好了 */ //取得当前用户
Subject currentUser=SecurityUtils.getSubject(); //使用shiro来进行登陆验证
if(!currentUser.isAuthenticated()) {
UsernamePasswordToken token=new UsernamePasswordToken("fuwh","123456");
try {
currentUser.login(token);
log.info("登陆成功!!!");
} catch (Exception e) {
e.printStackTrace();
log.error("认证失败...");
}
} currentUser.logout();
}
}
执行结果:
2017-08-26 15:16:03,357 [main] INFO [com.mchange.v2.log.MLog] - MLog clients using log4j logging.
2017-08-26 15:16:04,107 [main] INFO [com.mchange.v2.c3p0.C3P0Registry] - Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10]
2017-08-26 15:16:04,436 [main] INFO [org.apache.shiro.config.IniSecurityManagerFactory] - Realms have been explicitly set on the SecurityManager instance - auto-setting of realms will not occur.
2017-08-26 15:16:04,560 [main] INFO [com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource] - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hgetj59q83i1dm1es8ork|67f89fa3, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hgetj59q83i1dm1es8ork|67f89fa3, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/db_shiro, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
2017-08-26 15:16:05,040 [main] INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler...
2017-08-26 15:16:05,056 [main] INFO [com.fuwh.demo.ShiroDemo02] - 登陆成功!!!
此时,已经可以从表中去验证登陆了。那处理流程又是什么样的呢?
首先我们使用shiro_jdbc.ini来初始化了SecurityManager,在配置文件中,我们定义了连接池信息,还有jdbcRealm,同时在SecurityManager中指定了realm为定义的JjdbcRealm,这时候,其实shiro使用的SecurityManager是一个RealmSecurityManager的实例。而当我们登陆的时候,则会通过配置的jdbcRealm来从数据库中取得用户信息来进行认证。那是怎么取得呢?我们明明没有写sql什么的。
其实,看org.apache.shiro.realm.jdbc.JdbcRealm的源码可以看到,在这个类里面定义了很多的静态sql变量,点此查看,点此查看sql内容
其中比较重要的是AuthenticationQuery这个字段,默认情况下它的值是等于Default_Authentication_query。
Default_Authentication_Query="select password from users where username=?";
所以在默认情况下,它会从users这个表中通过username这个key来查找password。从而拿来跟我们提交的密码来进行匹配验证。
如果我们不想使用默认的数据库,默认的表名,默认的列名的话,也可以通过在配置文件中重写AuthenticationQuery的值来个性化sql文。
首先修改新建一个表members:
USE `db_shiro`; DROP TABLE IF EXISTS `members`; CREATE TABLE `members` (
`id` INT(4) NOT NULL AUTO_INCREMENT,
`userName` VARCHAR(20) DEFAULT NULL,
`pass` VARCHAR(100) DEFAULT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO `members`(`id`,`userName`,`pass`) VALUES (1,'fuwh','');
然后修改shiro_jdbc_sql.ini配置文件:
[main]
dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
dataSource.driverClass=com.mysql.jdbc.Driver
dataSource.jdbcUrl=jdbc:mysql://localhost:3306/db_shiro
dataSource.user=root
dataSource.password=rootadmin ;this is comment read from mysql
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSource
jdbcRealm.authenticationQuery=select pass from members where userName=?
securityManager.realms=$jdbcRealm
修改认证程序的配置文件:
package com.fuwh.demo; import org.apache.shiro.SecurityUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory; public class ShiroDemoSql02 { private static Logger log=LoggerFactory.getLogger(ShiroDemoSql02.class);
public static void main(String[] args) {
//取得SecurityManager工厂
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro_jdbc_sql.ini");
//取得SecurityManager实例
SecurityManager securityManager=factory.getInstance();
//将securityManager绑定到SecurityUtil
SecurityUtils.setSecurityManager(securityManager); /* 至此为止,简单的从mysql数据库读取realm信息的shiro环境就配置好了 */ //取得当前用户
Subject currentUser=SecurityUtils.getSubject(); //使用shiro来进行登陆验证
if(!currentUser.isAuthenticated()) {
UsernamePasswordToken token=new UsernamePasswordToken("fuwh","123");
try {
currentUser.login(token);
log.info("登陆成功!!!");
} catch (Exception e) {
e.printStackTrace();
log.error("认证失败...");
}
} currentUser.logout();
}
}
后面我们会讲到角色认证也是同样的道理。
自定义Realm
上面我们使用的是Shiro提供的默认的Realm,下面我们自定义一个从数据库中读取信息的Realm,通过继承AuthorizingRealm。
package com.fuwh.realm; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; 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 com.fuwh.util.DbUtil; public class MyJdbcRealm extends AuthorizingRealm{ @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// TODO Auto-generated method stub
return null;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
Connection conn=DbUtil.getConnection();
String sql="select * from members2 where username=?";
try {
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1, token.getPrincipal().toString());
ResultSet rs=ps.executeQuery();
while(rs.next()) {
AuthenticationInfo info=new SimpleAuthenticationInfo(rs.getString("username"),rs.getString("password"),"salt");
return info;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} return null;
} }
修改配置文件
[main]
myJdbcRealm=com.fuwh.realm.MyJdbcRealm
securityManager.realms=$myJdbcRealm
编写登陆类:
package com.fuwh.demo; import org.apache.shiro.SecurityUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory; public class ShiroDemoMySql02 { private static Logger log=LoggerFactory.getLogger(ShiroDemoMySql02.class);
public static void main(String[] args) {
//取得SecurityManager工厂
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro_jdbc_my_sql.ini");
//取得SecurityManager实例
SecurityManager securityManager=factory.getInstance();
//将securityManager绑定到SecurityUtil
SecurityUtils.setSecurityManager(securityManager); /* 至此为止,简单的从mysql数据库读取realm信息的shiro环境就配置好了 */ //取得当前用户
Subject currentUser=SecurityUtils.getSubject(); //使用shiro来进行登陆验证
if(!currentUser.isAuthenticated()) {
UsernamePasswordToken token=new UsernamePasswordToken("fuwh","123");
try {
currentUser.login(token);
log.info("登陆成功!!!");
} catch (Exception e) {
e.printStackTrace();
log.error("认证失败...");
}
} currentUser.logout();
}
}
那如果我们定义了多个Realm呢?
在编写一个Realm类:
package com.fuwh.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; public class MyJdbcRealm2 extends AuthorizingRealm{
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// TODO Auto-generated method stub
return null;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
return new SimpleAuthenticationInfo(token.getPrincipal(),"1234","salt");
}
}
修改shiro_jdbc_my_sql_2.ini配置文件:
[main]
myJdbcRealm=com.fuwh.realm.MyJdbcRealm
myJdbcRealm2=com.fuwh.realm.MyJdbcRealm2
authStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
securityManager.realms=$myJdbcRealm,$myJdbcRealm2
securityManager.authenticator.authenticationStrategy=$authStrategy
修改登陆类:
package com.fuwh.demo; import org.apache.shiro.SecurityUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory; public class ShiroDemoMySql02_2 { private static Logger log=LoggerFactory.getLogger(ShiroDemoMySql02_2.class);
public static void main(String[] args) {
//取得SecurityManager工厂
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro_jdbc_my_sql_2.ini");
//取得SecurityManager实例
SecurityManager securityManager=factory.getInstance();
//将securityManager绑定到SecurityUtil
SecurityUtils.setSecurityManager(securityManager); /* 至此为止,简单的从mysql数据库读取realm信息的shiro环境就配置好了 */ //取得当前用户
Subject currentUser=SecurityUtils.getSubject(); //使用shiro来进行登陆验证
if(!currentUser.isAuthenticated()) {
UsernamePasswordToken token=new UsernamePasswordToken("fuwh","123");
try {
currentUser.login(token);
log.info("登陆成功!!!");
} catch (Exception e) {
e.printStackTrace();
log.error("认证失败...");
}
} currentUser.logout();
}
}
上面的例子中使用的认证策略是只有一个成功就认为是成功。
在Shiro中还提供了下面几个策略:
FirstSuccessfulStrategy:只有第一个认证成功的信息会被用
AllSuccessfulStrategy:只有当所有的都成功的时候才认为是认证成功。
源码地址:https://github.com/oukafu/shiro
Shrio认证详解+自定义Realm的更多相关文章
- MySQL权限授权认证详解
MySQL权限授权认证详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.MySQL权限系统介绍1>.权限系统的作用是授予来自某个主机的某个用户可以查询.插入.修改.删除 ...
- JWT(Json web token)认证详解
JWT(Json web token)认证详解 什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该to ...
- Shiro认证详解
Shiro shiro是一个java的安全框架 官网地址 http://shiro.apache.org/ 目录 Shiro Shiro综述 过滤器 注解 整合Shiro 1. 配置SecurityM ...
- Shiro第二篇【介绍Shiro、认证流程、自定义realm、自定义realm支持md5】
什么是Shiro shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证.用户授权. spring中有spring security (原名Acegi),是一个权限框架,它和sp ...
- OAuth2.0认证详解
目录 什么是OAuth协议 OAuth2.0是为了解决什么问题? OAuth2.0成员和授权基本流程 OAuth2.0成员 OAuth2.0基本流程 什么是OAuth协议 OAuth 协议为用户资源的 ...
- OAuth 2.0 授权认证详解
一.认识 OAuth 2.0 1.1 OAuth 2.0 应用场景 OAuth 2.0 标准目前被广泛应用在第三方登录场景中,以下是虚拟出来的角色,阐述 OAuth2 能帮我们干什么,引用阮一峰这篇理 ...
- ASP.NET Forms身份认证详解
ASP.NET身份认证基础 在开始今天的内容之前,我想有二个最基础的问题首先要明确: 1. 如何判断当前请求是一个已登录用户发起的? 2. 如何获取当前登录用户的登录名? 在标准的ASP.NET身份认 ...
- Django自带表User认证详解
认证登陆(附方法实现代码,百度网盘拉取即可下载,激活码:gqt1) 在进行用户登陆验证的时候,如果是自己写代码,就必须要先查询数据库,看用户输入的用户名是否存在于数据库中: 如果用户存在于数据库中,然 ...
- angular指令详解--自定义指令
自定义指令 directive()这个方法是用来定义指令的: angular.module('myApp', []) .directive('myDirective', function ($time ...
随机推荐
- 编写高质量代码改善C#程序的157个建议:第17个建议之多数情况下使用foreach进行循环遍历
今天是我看<编写高质量代码:改善C#程序的157个建议>第二遍的时候了,看完这本书的确是受益匪浅,学到了很多东西,也明白了很多道理. 里面的代码我每个都调试了一遍,有时候是有些出入的,可能 ...
- 计算 x y 的最近值
计算xy的最近值. 代码如下: package Day05; import java.util.Arrays; public class FindNearestPoints { public stat ...
- 第2章 rsync算法原理和工作流程分析
本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释. 以下是本文的姊妹篇: 1.rsync(一):基本命令和用法 2.rsync(二):ino ...
- (转)log4j(六)——log4j.properties简单配置样例说明
一:测试环境与log4j(一)——为什么要使用log4j?一样,这里不再重述 1 老规矩,先来个栗子,然后再聊聊感受 (1)使用配文件的方式,是不是感觉非常的清爽,如果不在程序中读取配置文件就更加的清 ...
- plsql 数据迁移——导出表结构,表数据,表序号
场景:项目开发完之后要部署在不同的环境进行测试,这时候就需要将数据库中的表结构,序号,数据进行迁移,这时候就需要能够熟练的使用plsql. 问题: 导出的表结构,在另一个数据库中无法导入 部分表的数据 ...
- [BZOJ 1500]维修数列 [Splay Tree从进阶到住院]
历尽艰辛终于A掉了这题QwQ 贴COGS评论区几句话=.= 策爷:"splay/块状链表的自虐题.".深刻理解到如果没有M倾向就不要去写这题了.. -Chenyao2333 记得b ...
- python爬取百度搜索结果ur汇总
写了两篇之后,我觉得关于爬虫,重点还是分析过程 分析些什么呢: 1)首先明确自己要爬取的目标 比如这次我们需要爬取的是使用百度搜索之后所有出来的url结果 2)分析手动进行的获取目标的过程,以便以程序 ...
- iOS底层学习-KVC使用实践以及实现原理
简介 KVC(Key-value coding)键值编码,顾名思义.额,简单来说,是可以通过对象属性名称(Key)直接给属性值(value)编码(coding)"编码"可以理解为& ...
- Java学生成绩
import java.util.*; public class guanlixiton { public static void main(String[] args) { Scanner in = ...
- 笨办法用js屏蔽被http劫持的浮动广告
最近发现网站经常在右下角弹出一个浮动广告,开始的时候以为只是浏览器的广告. 后来越来越多同事反映在家里不同浏览器也会出现广告.然后深入检查了下,发现网站竟然被劫持了. 然后百度了一大堆资料,什么htt ...