java|java web网站中使用shiro实现前后台登录功能
年前公司网站需要重新开发一个新版本,在架构设计上废了一些劲,我项目架构大概是这样:Spring作为容器,Mybatis做数据持久层,SpringMVC做控制层,Shiro做为安全框架,页面使用tomdjs引擎,大概是这样,由于是功能型网站,需要有前后台登录,后台需要审核以及数据统计和分析等等,所以涉及到了两端登录,这里不多说,直接上关键代码:
在shiro实现登录功能时,Subject的实现类会调用Authenticator这个接口的默认实现类ModularRealmAuthenticator来进行帐号密码以及验证码的验证,这个接口会取得Realm接口的实现类的List进行while循环验证,问题就在这,如果用List我们是搞不清楚前台后台应该用哪一个Realm来验证的,所以我重新实现了一下这个接口:
注:credentialsMatcher这个东西是密码的验证规则,代码没什么含量我就不贴了,大家查一下shiro的文档就明白
package com.icc.base.shiro.authenticator;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
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.UnknownAccountException;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.authc.pam.UnsupportedTokenException;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.icc.base.emun.LoginEnum;
import com.icc.base.exception.CaptchaException;
import com.icc.base.shiro.authc.CaptchaAuthenticationToken;
/**
* 【java|java web网站中使用shiro实现前后台登录功能】 * Name:DefineModularRealmAuthenticator.java
*
* * Description: 自定义realm的验证规则 只要验证匹配直接返回
*
*
* @author Jason
* @see ModularRealmAuthenticator
* @date 2016-1-25 下午12:46:24
* @Modified By:
* @Modified Date:
* @version 1.0.0
*/
public class DefineModularRealmAuthenticator extends ModularRealmAuthenticator { private static final Logger log = LoggerFactory
.getLogger(DefineModularRealmAuthenticator.class);
/**
* 将Realm的实现类作为Map传入,这样便可以分清除前后台登录
*/
private Map defineRealms;
/**
* 调用单个Realm来进行验证
*/
@Override
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 = null;
try {
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);
}
}catch (CaptchaException e) {
throw e;
} catch (IncorrectCredentialsException e) {
throw e;
}catch (UnknownAccountException e) {
throw e;
}catch (Throwable throwable) {
if (log.isDebugEnabled()) {
String msg = "Realm ["
+ realm
+ "] threw an exception during a multi-realm authentication attempt:";
log.debug(msg,throwable );
}}return info;
}
/**
* 判断Realm是不是null
*/
@Override
protected void assertRealmsConfigured() throws IllegalStateException {
defineRealms = getDefineRealms();
if (CollectionUtils.isEmpty(defineRealms)) {
String msg = "Configuration error:No realms have been configured!One or more realms must be "
+ "present to execute an authentication attempt.";
throw new IllegalStateException(msg);
}
}
/**
* 这个方法比较重要,用来判断此次调用是前台还是后台
*/
@Override
protected AuthenticationInfo doAuthenticate(
AuthenticationToken authenticationToken)
throws AuthenticationException {
assertRealmsConfigured();
CaptchaAuthenticationToken token = (CaptchaAuthenticationToken) authenticationToken;
Realm realm = null;
// 前端登录
if (StringUtils.equals(token.getLoginType(),
LoginEnum.CUSTOMER.toString())) {
realm = (Realm) defineRealms.get("customerRealm");
}
// 后台登录
if (StringUtils
.equals(token.getLoginType(), LoginEnum.ADMIN.toString())) {
realm = (Realm) defineRealms.get("adminRealm");
}
if(realm==null){
return null;
}
return doSingleRealmAuthentication(realm, authenticationToken);
} public void setDefineRealms(Map defineRealms) {
this.defineRealms = defineRealms;
} public Map getDefineRealms() {
return defineRealms;
}
}
这里需要解释一下的是这个参数authenticationToken,我自己实现了这个接口,看代码:
package com.icc.base.shiro.authc;
import org.apache.shiro.authc.UsernamePasswordToken;
/**
* *
*@Program Name: IWill.com.icc.base.shiro.authc.CaptchaAuthenticationToken.java
*@Written by: Jason
*@Creation Date : 2016年3月1日 上午1:13:06
*@version: v1.00
*@Description:封装登录数据
*
*
*
*@ModificationHistory
*WhoWhenWhat
*------------------------------------------------------------------
*Jason2016年3月1日上午1:13:06TODO
*
*
**
*/
public class CaptchaAuthenticationToken extends UsernamePasswordToken { private String captcha;
/**
* 用来区分前后台登录的标记
*/
private String loginType;
public CaptchaAuthenticationToken(String username, String password, boolean rememberMe, String host, String captcha) {
super(username, password, rememberMe, host);
this.captcha = captcha;
} public String getCaptcha() {
return captcha;
} public void setCaptcha(String captcha) {
this.captcha = captcha;
} public String getLoginType() {
return loginType;
} public void setLoginType(String loginType) {
this.loginType = loginType;
}}
下面看一下登录的代码,大家应该就都明白了:
前端登录关键代码:
CaptchaAuthenticationToken token = new CaptchaAuthenticationToken(username, password, false, host, captcha);
token.setLoginType(LoginEnum.CUSTOMER.toString());
这里传入了前端登陆的标记,结合上面代码doAuthenticate()方法的判断,应该没什么不明白的.
这是后端登录的关键代码:
CaptchaAuthenticationToken token = new CaptchaAuthenticationToken(username, password, false, host, captcha);
token.setLoginType(LoginEnum.ADMIN.toString());
没什么问题吧,至于Realm的代码我就不贴了,跟大家写的一样,以上都是关键代码,应该不难懂.
本人原创,只想帮大家解决一个问题,不喜勿喷,有问题欢迎一起讨论!
补充:
有朋友需要LoginEnum的代码,其实功能很简单,就是用标识来区分2种登录的,下面看代码就明白了:
public enum LoginEnum { CUSTOMER("1"),ADMIN("2");
private String type;
private LoginEnum(String type){
this.type = type;
}
@Override
publicString toString(){
return this.type.toString();
}
}
很简单吧. 关于xml配置的问题,以上是全部shiro的配置,我的项目目录结构是每一个框架单独做一个xml文件,然后用一个总的xml来包含所有文件,最后在web.xml里加载一下这个总的xml就可以了,shiro的web.xml配置与平常配置一样
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 事件代理
- Java|Java OpenCV图像处理之SIFT角点检测详解
- java中如何实现重建二叉树
- 数组常用方法一
- 【Hadoop踩雷】Mac下安装Hadoop3以及Java版本问题
- 【亲测好用】高逼格配色网站推荐
- 私有化轻量级持续集成部署方案--03-部署web服务(下)
- Java|Java基础——数组
- RxJava|RxJava 在Android项目中的使用(一)