springboot下使用shiro自定義filter的個(gè)人經(jīng)驗(yàn)分享
在springboot中使用shiro,由于沒有了xml配置文件,因此使用的方法與spring中有些區(qū)別。在踩了無數(shù)個(gè)坑后,在此將springboot下使用shiro的步驟總結(jié)如下。
由于本人對shiro的了解不是很深入,在實(shí)現(xiàn)了工作需求后就沒有繼續(xù)研究了,因此可能存在遺漏的地方或有錯(cuò)誤的地方,還請多包涵。
目標(biāo)
- 在springboot中使用shiro
- 1.實(shí)現(xiàn)用戶的登錄驗(yàn)證
- 2.對于一些指定的url使用自定義的filter驗(yàn)證方式(不再使用shiro的realm驗(yàn)證)
步驟
1.在pom.xml中添加shiro的依賴
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency>
2.創(chuàng)建ShiroRealm.java
繼承AuthorizingRealm類,重寫登錄認(rèn)證方法與授權(quán)方法
import User;
import UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
public class ShiroRealm extends AuthorizingRealm {
//自己編寫的service,注入,執(zhí)行數(shù)據(jù)庫查詢方法用
@Autowired
private UserService userService;
//認(rèn)證.登錄
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken utoken=(UsernamePasswordToken) token;//獲取用戶輸入的token
String username = utoken.getUsername();
//這個(gè)User對象為自定義的JavaBean,使用userService從數(shù)據(jù)庫中得到User對象(包含用戶名、密碼、權(quán)限3個(gè)字段)
User user = userService.findUserByUserName(username);
return new SimpleAuthenticationInfo(user, user.getPassword(),this.getClass().getName());//放入shiro.調(diào)用CredentialsMatcher檢驗(yàn)密碼
}
//授權(quán)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
User user=(User) principal.fromRealm(this.getClass().getName()).iterator().next();//獲取session中的用戶
List<String> permissions=new ArrayList<>();
//使用自定義的user對象獲得權(quán)限字段,string類型,裝入集合
permissions.add(user.getRole());
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.addStringPermissions(permissions);//將權(quán)限放入shiro中.(我的代碼中其實(shí)沒有用到權(quán)限相關(guān))
return info;
}
}
3.創(chuàng)建ShiroConfiguration.java
使用@Configuration注解,是shiro的配置類,類似xml
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
@Configuration
public class ShiroConfiguration {
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必須設(shè)置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
/*重要,設(shè)置自定義攔截器,當(dāng)訪問某些自定義url時(shí),使用這個(gè)filter進(jìn)行驗(yàn)證*/
Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
//如果map里面key值為authc,表示所有名為authc的過濾條件使用這個(gè)自定義的filter
//map里面key值為myFilter,表示所有名為myFilter的過濾條件使用這個(gè)自定義的filter,具體見下方
filters.put("myFilter", new MyFilter());
shiroFilterFactoryBean.setFilters(filters);
/*---------------------------------------------------*/
//攔截器
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
//配置退出過濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實(shí)現(xiàn)了
filterChainDefinitionMap.put("/logout", "logout");
// anon:所有url都都可以匿名訪問;
// authc: 需要認(rèn)證才能進(jìn)行訪問;
// user:配置記住我或認(rèn)證通過可以訪問;
//放開靜態(tài)資源的過濾
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
//放開登錄url的過濾
filterChainDefinitionMap.put("/loginController", "anon");
///
//對于指定的url,使用自定義filter進(jìn)行驗(yàn)證
filterChainDefinitionMap.put("/targetUrl", "myFilter");
//可以配置多個(gè)filter,用逗號分隔,按順序過濾,下方表示先通過自定義filter的驗(yàn)證,再通過shiro默認(rèn)過濾器的驗(yàn)證
//filterChainDefinitionMap.put("/targetUrl", "myFilter,authc");
///
//過濾鏈定義,從上向下順序執(zhí)行,一般將 /**放在最為下邊
//url從上向下匹配,當(dāng)條件匹配成功時(shí),就會(huì)進(jìn)入指定filter并return(不會(huì)判斷后續(xù)的條件),因此這句需要在最下邊
filterChainDefinitionMap.put("/**", "authc");
//如果不設(shè)置默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.jsp"頁面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登錄成功后要跳轉(zhuǎn)的鏈接
shiroFilterFactoryBean.setSuccessUrl("/loginSuccess");
// 未授權(quán)界面
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
return securityManager;
}
//配置核心安全事務(wù)管理器
@Bean(name="securityManager")
public SecurityManager securityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {
DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
manager.setRealm(shiroRealm);
return manager;
}
//配置自定義的權(quán)限登錄器
@Bean(name="shiroRealm")
public ShiroRealm shiroRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) {
ShiroRealm shiroRealm=new ShiroRealm();
shiroRealm.setCredentialsMatcher(matcher);
return shiroRealm;
}
//配置自定義的密碼比較器
@Bean(name="credentialsMatcher")
public CredentialsMatcher credentialsMatcher() {
return new CredentialsMatcher();
}
}
4.創(chuàng)建自定義的過濾器MyFilter.java
繼承AccessControlFilter類,在步驟3中使用
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Set;
public class MyFilter extends AccessControlFilter {
private static Logger log = LoggerFactory.getLogger(MyFilter.class);
//判斷是否攔截,false為攔截,true為允許
@Override
public boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object object) throws Exception {
Subject subject = getSubject(req,resp);
String url = getPathWithinApplication(req);
log.info("當(dāng)前用戶正在訪問的url為 " + url);
log.info("subject.isPermitted(url);"+subject.isPermitted(url));
//可自行根據(jù)需要判斷是否攔截,可以獲得subject判斷用戶權(quán)限,也可以使用req獲得請求頭請求體信息
//return true;
return false;
}
//上面的方法返回false后(被攔截),會(huì)進(jìn)入這個(gè)方法;這個(gè)方法返回false表示處理完畢(不放行);返回true表示需要繼續(xù)處理(放行)
@Override
public boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//從req中獲得的值,也可以自己使用其它判斷是否放行的方法
String username = request.getParameter("name");
String password = request.getParameter("password");
//創(chuàng)建token對象
UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.getSubject();
try {
//使用subject對象的login方法驗(yàn)證該token能否登錄(使用方法2中的shiroRealm.java中的方法驗(yàn)證)
subject.login(usernamePasswordToken);
} catch (Exception e) {
//log.info("登陸失敗");
//log.info(e.getMessage());
return false;
}
//log.info("登陸成功");
return true;
}
}
5.步驟3中使用了自定義密碼驗(yàn)證的方式
因此需要?jiǎng)?chuàng)建類CredentialsMatcher.java(與步驟3中的名稱對應(yīng)),繼承SimpleCredentialsMatcher類
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
public class CredentialsMatcher extends SimpleCredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken utoken=(UsernamePasswordToken) token;
//獲得用戶輸入的密碼:(可以采用加鹽(salt)的方式去檢驗(yàn))
String inPassword = new String(utoken.getPassword());
//獲得數(shù)據(jù)庫中的密碼
String dbPassword=(String) info.getCredentials();
//進(jìn)行密碼的比對
return this.equals(inPassword, dbPassword);
}
}
6.步驟3中放開了對登錄頁/loginController的過濾
因此我增加了一個(gè)ShiroController.java類
import User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
@Controller
public class ShiroController {
@RequestMapping("/")
public String loginPage() {
return "login";
}
@RequestMapping("/login")
public String login() {
return "login";
}
@RequestMapping("/loginController")
public String loginUser(String username,String password,HttpSession session) {
UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(usernamePasswordToken); //完成登錄
//自定義的JavaBean,用于保存用戶名、密碼、權(quán)限3個(gè)字段
User user=(User) subject.getPrincipal();
//可選,可放入session,以備后續(xù)使用
session.setAttribute("user", user);
//跳轉(zhuǎn)到登錄成功頁(controller)
return "forward:/loginSuccess";
} catch(Exception e) {
//登錄失敗,跳轉(zhuǎn)回登錄頁(html)
return "login";
}
}
@RequestMapping("/logOut")
public String logOut(HttpSession session) {
Subject subject = SecurityUtils.getSubject();
subject.logout();
//session.removeAttribute("user");
return "login";
}
}
個(gè)人經(jīng)驗(yàn)
1.shiro配置類中的url攔截的執(zhí)行順序?yàn)閺纳系较?,如果url匹配到一個(gè)規(guī)則,則會(huì)跳出匹配方法,忽略后續(xù)的匹配規(guī)則(相當(dāng)于return)。
2.shiro使用自定義filter時(shí),最好繼承shiro的filter,不要直接繼承Filter類。
3.shiro使用自定義filter時(shí),map集合的key配置為"authc"、value配置為"new MyFilter()"時(shí),表示對配置為authc的url使用自定義filter進(jìn)行攔截,而不會(huì)使用ShiroRealm中的驗(yàn)證方法驗(yàn)證(可能是將shiro默認(rèn)的authc的攔截器覆蓋了);因此最好將key配置為其它自定義的字符串,將部分url的攔截規(guī)則設(shè)置為使用自定義filter攔截即可(如果仍想使用shiro默認(rèn)的攔截器,可用逗號連接"authc")。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用Spirng Boot Admin監(jiān)控Spring Cloud應(yīng)用項(xiàng)目
這篇文章主要介紹了使用Spirng Boot Admin監(jiān)控Spring Cloud應(yīng)用項(xiàng)目,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
Eureka源碼閱讀解析Server服務(wù)端啟動(dòng)流程實(shí)例
這篇文章主要為大家介紹了Eureka源碼閱讀解析Server服務(wù)端啟動(dòng)流程實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
利用Spring Session和redis對Session進(jìn)行共享詳解
這篇文章主要給大家介紹了關(guān)于利用Spring、Session和redis對Session進(jìn)行共享的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09

