web项目总免不了用户的管理与注册,需求稍微再多一点儿,就涉及用户的角色及权限管理了,下面根据自己项目的实际经验,介绍如何在Jfinal项目中使用Shiro来进行简单的登陆及权限管理。
主角简介
- Jfinal 位居开源中国年度热门开源项目前列,简单好用快速的java web开发框架,用过就知道。
- Shiro Apache基金会顶级项目,所以你懂得。java安全框架里的主流选择,号称相当简单,但是我至今其实对一些概念还稀里糊涂,所以本文也只记录使用,不做原理概念分析,入门参见教程
使用
1 方案选择
根据项目需求设计角色及权限管理方案,我用到的几乎是最简单的了,如下图所示:

2 引入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插件
jfinal-shiro-2.0.0.jar 到该目录下。
3 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());
}
4 实现Realm
下面是我的代码供参考
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());
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
5 配置shiro.ini
该文件需放在 /WEB-INF/shiro.ini这个位置,下面是我的shiro.ini 供参考:
[main]
#realm
dbRealm = com.learnShiro.biz.shiro.DbRealm
securityManager.realm = $dbRealm
6 配置web.xml
在所有filter前面加上shiro的filter
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<filter>
<filter-name>shiro</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>shiro</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
7 继续
已经啰啰嗦嗦的配置这么多了,继续干嘛?赶紧开始用啊!!!
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");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
//需要角色是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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 解释一下Shiro共有5个注解,分别如下:
RequiresAuthentication :使用该注解标注的类,实例,方法在访问或调用时,当前Subject必须在当前session中已经过认证。
RequiresGuest :使用该注解标注的类,实例,方法在访问或调用时,当前Subject可以是“guest”身份,不需要经过认证或者在原先的session中存在记录。
RequiresPermissions :当前Subject需要拥有某些特定的权限时,才能执行被该注解标注的方法。如果当前Subject不具有这样的权限,则方法不会被执行。
RequiresRoles :当前Subject必须拥有所有指定的角色时,才能访问被该注解标注的方法。如果当天Subject不同时拥有所有指定角色,则方法不会执行还会抛出AuthorizationException异常。
RequiresUser :当前Subject必须是应用的用户,才能访问或调用被该注解标注的类,实例,方法。
参考文章
The end
|