您的位置:首页 > 编程语言 > Java开发

Shiro-学习总结-认证

2017-02-20 22:19 281 查看
网上找到一张认证的相关图,如下



shiro第一个例子,参考quickstart的例子,相关文档可以去shiro官网下载

首先创建一个maven的工程,pom.xml文件内容如下:

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>

</dependencies>

<build>
<finalName>shiro</finalName>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/${project.build.finalName}</path>
</configuration>
</plugin>
</plugins>
</build>


java代码程序:

public class Quickstart {

private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);

public static void main(String[] args) {
//构造SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();

SecurityUtils.setSecurityManager(securityManager);
//获取当前subject
Subject currentUser = SecurityUtils.getSubject();
//获取session
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
//测试当前用户是否已经登录即执行认证
if (!currentUser.isAuthenticated()) {
//用户名、密码封装为UsernamePasswordToken
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//记住我
token.setRememberMe(true);
try {
//登录即执行认证
currentUser.login(token);
} catch (UnknownAccountException uae) {//没有此账户
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {//密码错误
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {//账户被锁定
log.info("The account for username " + token.getPrincipal() + " is locked.  " +
"Please contact your administrator to unlock it.");
}
catch (AuthenticationException ae) {
}
}
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

//判断是否有某个角色
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//判断是否有某个权限
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring.  Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//退出登录
currentUser.logout();
System.exit(0);
}
}


总结:

1、创建默认的securityManager,会默认注入iniRealm即表示从配置文件中获取用户相关信息。源码为:

protected Realm createRealm(Ini ini) {
//IniRealm realm = new IniRealm(ini); changed to support SHIRO-322
IniRealm realm = new IniRealm();//默认为inirealm
realm.setName(INI_REALM_NAME);
realm.setIni(ini); //added for SHIRO-322
return realm;
}


2、用户通过currentUser.login(token)方法进行认证,内部调用securityManager.login(this, token),securityManager则是通过调用Authenticator的  public AuthenticationInfoauthenticate(AuthenticationToken
authenticationToken)  throws AuthenticationException;方法进行认证。而Authenticator是一个接口,其接口实现类有如下图所示:



认证程序最终会去调用ModularRealmAuthenticator里的doAuthenticate(AuthenticationToken authenticationToken)方法【authenticationToken为用户输入的用户名,密码】。doAuthenticate方法源码如下:

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
Collection<Realm> realms = getRealms();
if (realms.size() == 1) {
return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);//单个realm
} else {
return doMultiRealmAuthentication(realms, authenticationToken);//多个realm
}
}

之后在根据realm个数,进行不同方法的调用:

//单个realm  
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
if (!realm.supports(token)) {
String msg = "Realm [" + realm + "] does not support authentication token [" +
token + "].  Please ensure that the appropriate Realm implementation is " +
"configured correctly or that the realm accepts AuthenticationTokens of this type.";
throw new UnsupportedTokenException(msg);
}
AuthenticationInfo info = realm.getAuthenticationInfo(token);
if (info == null) {
String msg = "Realm [" + realm + "] was unable to find account data for the " +
"submitted AuthenticationToken [" + token + "].";
throw new UnknownAccountException(msg);
}
return info;
}


//多个realm
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {

AuthenticationStrategy strategy = getAuthenticationStrategy();

AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);

if (log.isTraceEnabled()) {
log.trace("Iterating through {} realms for PAM authentication", realms.size());
}

for (Realm realm : realms) {

aggregate = strategy.beforeAttempt(realm, token, aggregate);

if (realm.supports(token)) {

log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);

AuthenticationInfo info = null;
Throwable t = null;
try {
info = realm.getAuthenticationInfo(token);
} catch (Throwable throwable) {
t = throwable;
if (log.isDebugEnabled()) {
String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
log.debug(msg, t);
}
}

aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);

} else {
log.debug("Realm [{}] does not support token {}.  Skipping realm.", realm, token);
}
}

aggregate = strategy.afterAllAttempts(token, aggregate);

return aggregate;
}
无论单个还是多个realm都会调用realm.getAuthenticationInfo(token),此时的realm默认为org.apache.shiro.realm.SimpleAccountRealm。代码如下:
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

AuthenticationInfo info = getCachedAuthenticationInfo(token);
if (info == null) {
//otherwise not cached, perform the lookup:
info = doGetAuthenticationInfo(token);
log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
if (token != null && info != null) {
cacheAuthenticationInfoIfPossible(token, info);
}
} else {
log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
}

if (info != null) {
assertCredentialsMatch(token, info);//默认认证方法为SimpleCredentialsMatcher
} else {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}

return info;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java shiro