web项目总免不了用户的管理与注册,需求稍微再多一点儿,就涉及用户的角色及权限管理了,下面根据自己项目的实际经验,介绍如何在Jfinal项目中使用Shiro来进行简单的登陆及权限管理。
根据项目需求设计角色及权限管理方案,我用到的几乎是最简单的了,如下图所示:
shiro-core-1.2.4.jar
和shiro-web-1.2.4.jar
至项目WEB-INF/lib
目录下,同时确保shiro的依赖jar:slf4j,commons-beanutils,commons-logging
也位于该目录下(maven直接pom添加上面两个shiro的依赖就好)。jfinal-shiro-2.0.0.jar
到该目录下。DefaultConfig.java
DefaultConfig.java
中的public void configConstant(Constants me)
方法中加入401与403错误代码处理(可选)。//RequiresGuest,RequiresAuthentication,RequiresUser验证异常,返回HTTP401状态码
me.setErrorView(401, "/login.html");
//RequiresRoles,RequiresPermissions授权异常,返回HTTP403状态码
me.setErrorView(403, "/login.html");
class DefaultConfig
加一个成员变量public class DefaultConfig extends JFinalConfig {
/**
* 供Shiro插件使用。
*/
Routes routes;
public void configRoute(Routes me)
方法中加入:public void configRoute(Routes me) {
this.routes = me;
me.add(...)
...
public void configPlugin(Plugins me)
方法最后加入:public void configPlugin(Plugins me) {
...//other plugins
ShiroPlugin shiroPlugin = new ShiroPlugin(this.routes);
shiroPlugin.setLoginUrl("/login.do");//登陆url:未验证成功跳转
shiroPlugin.setSuccessUrl("/index.do");//登陆成功url:验证成功自动跳转
shiroPlugin.setUnauthorizedUrl("/login/needPermission");//授权url:未授权成功自动跳转
me.add(shiroPlugin);
}
public void configInterceptor(Interceptors me) {
me.add(new ShiroInterceptor());
}
下面是我的代码供参考
package com.learnShiro.biz.shiro;
import ...
public class DbRealm extends AuthorizingRealm {
public String getName() {
return "DbRealm";
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Model m = (Model) principals.fromRealm(getName()).iterator().next();
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
if( null == m){
return info;
}
Roles role=Roles.dao.findFirst("select * from roles where id = ? limit 1", m.getInt("roleid"));
if( null == role){
return info;
}
info.addRole(role.getStr("rolename"));
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
CaptchaUsernamePasswordToken authcToken = (CaptchaUsernamePasswordToken) token;
if (authcToken.getUsername()==null||StrKit.isBlank(authcToken.getUsername())) {
throw new AuthenticationException("用户名不可以为空");
}
String loginName=authcToken.getUsername();
String extraStr=authcToken.getExtra();
if (StringUtils.equals(extraStr, "admin")) {
Admin admin = Admin.dao.findFirst("select * from admin where loginname = ? and islock=0 limit 1",loginName);
if (null == admin) {
throw new AuthenticationException("用户名或者密码错误");
}else{
return new SimpleAuthenticationInfo(admin, admin.getStr("loginpass"), getName());
}
}else {
Students student = Students.dao.findFirst("select * from students where loginname = ? and enable =1 limit 1",loginName);
if (null == student) {
throw new AuthenticationException("用户名或者密码错误");
}else{
return new SimpleAuthenticationInfo(student, student.getStr("loginpass"), getName());
}
}
}
}
该文件需放在 /WEB-INF/shiro.ini这个位置,下面是我的shiro.ini 供参考:
[main]
#realm
dbRealm = com.learnShiro.biz.shiro.DbRealm
securityManager.realm = $dbRealm
在所有filter前面加上shiro的filter
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListenerlistener-class>
listener>
<filter>
<filter-name>shirofilter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilterfilter-class>
filter>
<filter-mapping>
<filter-name>shirofilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
已经啰啰嗦嗦的配置这么多了,继续干嘛?赶紧开始用啊!!!
public void doLogin() {
String username = getPara("loginName");
String password = getPara("password");
try {
password = LoginUtils.genEncryptPass(password, username);
String rememberMeStr = getPara("rememberMe");
boolean rememberMe=false;
if (StringUtils.equals(rememberMeStr, "on")) {
rememberMe=true;
}
//验证码
if (!validateCaptcha("captcha")) {
this.setAttr("loginError", "验证码错误");
this.keepPara();
this.forwardAction("/login");
return;
}
CaptchaUsernamePasswordToken token = new
CaptchaUsernamePasswordToken(username, password,rememberMe,"","",extraStr);
Subject subject = SecurityUtils.getSubject();
// 进行用用户名和密码验证,如果验证不过会throw exception
subject.login(token);
if (extraStr.equals("admin")) {
//save admin session
Admin admin = Admin.dao.findFirst("select * from admin where loginname = ? limit 1",username);
setSessionAttr("admin", admin);
// 调转到admin主页面
this.redirect("/admin");
}else {
//save student session
Students student = Students.dao.findFirst("select * from students where loginname = ? limit 1",username);
setSessionAttr("student", student);
// 调转到user主页面
this.redirect("/user");
}
} catch (Exception e) {
this.setAttr("loginError", "用户名或密码错误");
this.keepPara();
if (extraStr.equals("admin")) {
this.forwardAction("/login/adminLogin");
}else{
this.forwardAction("/login");
}
}
}
//需要角色是admin和teacher的才能访问,否则跳转至授权url
@RequiresRoles(value = { "admin","teacher" },logical=Logical.OR)
public class AdminController extends Controller {
public void index() {
//...
}
}
///////////////////////
//需要通过验证(登陆成功)才能访问,否则跳转至登陆url
@RequiresAuthentication
public void logout() {
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.logout();
this.redirect("/login");
} catch (Exception e) {
log.debug("登出发生错误", e);
}
}
RequiresAuthentication
:使用该注解标注的类,实例,方法在访问或调用时,当前Subject必须在当前session中已经过认证。RequiresGuest
:使用该注解标注的类,实例,方法在访问或调用时,当前Subject可以是“guest”身份,不需要经过认证或者在原先的session中存在记录。RequiresPermissions
:当前Subject需要拥有某些特定的权限时,才能执行被该注解标注的方法。如果当前Subject不具有这样的权限,则方法不会被执行。RequiresRoles
:当前Subject必须拥有所有指定的角色时,才能访问被该注解标注的方法。如果当天Subject不同时拥有所有指定角色,则方法不会执行还会抛出AuthorizationException异常。RequiresUser
:当前Subject必须是应用的用户,才能访问或调用被该注解标注的类,实例,方法。The end