Jfinal配合Shiro進行許可權控制
阿新 • • 發佈:2019-02-04
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());
}
}
}
}
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");
}
}
}
- 需要驗證身份或授權才能訪問的地方,程式碼片段:
//需要角色是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);
}
}
- 解釋一下Shiro共有5個註解,分別如下:
RequiresAuthentication
:使用該註解標註的類,例項,方法在訪問或呼叫時,當前Subject必須在當前session中已經過認證。RequiresGuest
:使用該註解標註的類,例項,方法在訪問或呼叫時,當前Subject可以是“guest”身份,不需要經過認證或者在原先的session中存在記錄。RequiresPermissions
:當前Subject需要擁有某些特定的許可權時,才能執行被該註解標註的方法。如果當前Subject不具有這樣的許可權,則方法不會被執行。RequiresRoles
:當前Subject必須擁有所有指定的角色時,才能訪問被該註解標註的方法。如果當天Subject不同時擁有所有指定角色,則方法不會執行還會丟擲AuthorizationException異常。RequiresUser
:當前Subject必須是應用的使用者,才能訪問或呼叫被該註解標註的類,例項,方法。
參考文章
The end