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的更多相关文章

  1. MySQL权限授权认证详解

    MySQL权限授权认证详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.MySQL权限系统介绍1>.权限系统的作用是授予来自某个主机的某个用户可以查询.插入.修改.删除 ...

  2. JWT(Json web token)认证详解

    JWT(Json web token)认证详解 什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该to ...

  3. Shiro认证详解

    Shiro shiro是一个java的安全框架 官网地址 http://shiro.apache.org/ 目录 Shiro Shiro综述 过滤器 注解 整合Shiro 1. 配置SecurityM ...

  4. Shiro第二篇【介绍Shiro、认证流程、自定义realm、自定义realm支持md5】

    什么是Shiro shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证.用户授权. spring中有spring security (原名Acegi),是一个权限框架,它和sp ...

  5. OAuth2.0认证详解

    目录 什么是OAuth协议 OAuth2.0是为了解决什么问题? OAuth2.0成员和授权基本流程 OAuth2.0成员 OAuth2.0基本流程 什么是OAuth协议 OAuth 协议为用户资源的 ...

  6. OAuth 2.0 授权认证详解

    一.认识 OAuth 2.0 1.1 OAuth 2.0 应用场景 OAuth 2.0 标准目前被广泛应用在第三方登录场景中,以下是虚拟出来的角色,阐述 OAuth2 能帮我们干什么,引用阮一峰这篇理 ...

  7. ASP.NET Forms身份认证详解

    ASP.NET身份认证基础 在开始今天的内容之前,我想有二个最基础的问题首先要明确: 1. 如何判断当前请求是一个已登录用户发起的? 2. 如何获取当前登录用户的登录名? 在标准的ASP.NET身份认 ...

  8. Django自带表User认证详解

    认证登陆(附方法实现代码,百度网盘拉取即可下载,激活码:gqt1) 在进行用户登陆验证的时候,如果是自己写代码,就必须要先查询数据库,看用户输入的用户名是否存在于数据库中: 如果用户存在于数据库中,然 ...

  9. angular指令详解--自定义指令

    自定义指令 directive()这个方法是用来定义指令的: angular.module('myApp', []) .directive('myDirective', function ($time ...

随机推荐

  1. 编写高质量代码改善C#程序的157个建议:第17个建议之多数情况下使用foreach进行循环遍历

    今天是我看<编写高质量代码:改善C#程序的157个建议>第二遍的时候了,看完这本书的确是受益匪浅,学到了很多东西,也明白了很多道理. 里面的代码我每个都调试了一遍,有时候是有些出入的,可能 ...

  2. 计算 x y 的最近值

    计算xy的最近值. 代码如下: package Day05; import java.util.Arrays; public class FindNearestPoints { public stat ...

  3. 第2章 rsync算法原理和工作流程分析

    本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释. 以下是本文的姊妹篇: 1.rsync(一):基本命令和用法 2.rsync(二):ino ...

  4. (转)log4j(六)——log4j.properties简单配置样例说明

    一:测试环境与log4j(一)——为什么要使用log4j?一样,这里不再重述 1 老规矩,先来个栗子,然后再聊聊感受 (1)使用配文件的方式,是不是感觉非常的清爽,如果不在程序中读取配置文件就更加的清 ...

  5. plsql 数据迁移——导出表结构,表数据,表序号

    场景:项目开发完之后要部署在不同的环境进行测试,这时候就需要将数据库中的表结构,序号,数据进行迁移,这时候就需要能够熟练的使用plsql. 问题: 导出的表结构,在另一个数据库中无法导入 部分表的数据 ...

  6. [BZOJ 1500]维修数列 [Splay Tree从进阶到住院]

    历尽艰辛终于A掉了这题QwQ 贴COGS评论区几句话=.= 策爷:"splay/块状链表的自虐题.".深刻理解到如果没有M倾向就不要去写这题了.. -Chenyao2333 记得b ...

  7. python爬取百度搜索结果ur汇总

    写了两篇之后,我觉得关于爬虫,重点还是分析过程 分析些什么呢: 1)首先明确自己要爬取的目标 比如这次我们需要爬取的是使用百度搜索之后所有出来的url结果 2)分析手动进行的获取目标的过程,以便以程序 ...

  8. iOS底层学习-KVC使用实践以及实现原理

    简介 KVC(Key-value coding)键值编码,顾名思义.额,简单来说,是可以通过对象属性名称(Key)直接给属性值(value)编码(coding)"编码"可以理解为& ...

  9. Java学生成绩

    import java.util.*; public class guanlixiton { public static void main(String[] args) { Scanner in = ...

  10. 笨办法用js屏蔽被http劫持的浮动广告

    最近发现网站经常在右下角弹出一个浮动广告,开始的时候以为只是浏览器的广告. 后来越来越多同事反映在家里不同浏览器也会出现广告.然后深入检查了下,发现网站竟然被劫持了. 然后百度了一大堆资料,什么htt ...