springboot下使用shiro自定義filter的個人經(jīng)驗分享
在springboot中使用shiro,由于沒有了xml配置文件,因此使用的方法與spring中有些區(qū)別。在踩了無數(shù)個坑后,在此將springboot下使用shiro的步驟總結(jié)如下。
由于本人對shiro的了解不是很深入,在實現(xiàn)了工作需求后就沒有繼續(xù)研究了,因此可能存在遺漏的地方或有錯誤的地方,還請多包涵。
目標
- 在springboot中使用shiro
- 1.實現(xiàn)用戶的登錄驗證
- 2.對于一些指定的url使用自定義的filter驗證方式(不再使用shiro的realm驗證)
步驟
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類,重寫登錄認證方法與授權(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; //認證.登錄 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken utoken=(UsernamePasswordToken) token;//獲取用戶輸入的token String username = utoken.getUsername(); //這個User對象為自定義的JavaBean,使用userService從數(shù)據(jù)庫中得到User對象(包含用戶名、密碼、權(quán)限3個字段) User user = userService.findUserByUserName(username); return new SimpleAuthenticationInfo(user, user.getPassword(),this.getClass().getName());//放入shiro.調(diào)用CredentialsMatcher檢驗密碼 } //授權(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中.(我的代碼中其實沒有用到權(quá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(); // 必須設置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); /*重要,設置自定義攔截器,當訪問某些自定義url時,使用這個filter進行驗證*/ Map<String, Filter> filters = new LinkedHashMap<String, Filter>(); //如果map里面key值為authc,表示所有名為authc的過濾條件使用這個自定義的filter //map里面key值為myFilter,表示所有名為myFilter的過濾條件使用這個自定義的filter,具體見下方 filters.put("myFilter", new MyFilter()); shiroFilterFactoryBean.setFilters(filters); /*---------------------------------------------------*/ //攔截器 Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); //配置退出過濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實現(xiàn)了 filterChainDefinitionMap.put("/logout", "logout"); // anon:所有url都都可以匿名訪問; // authc: 需要認證才能進行訪問; // user:配置記住我或認證通過可以訪問; //放開靜態(tài)資源的過濾 filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/img/**", "anon"); //放開登錄url的過濾 filterChainDefinitionMap.put("/loginController", "anon"); /// //對于指定的url,使用自定義filter進行驗證 filterChainDefinitionMap.put("/targetUrl", "myFilter"); //可以配置多個filter,用逗號分隔,按順序過濾,下方表示先通過自定義filter的驗證,再通過shiro默認過濾器的驗證 //filterChainDefinitionMap.put("/targetUrl", "myFilter,authc"); /// //過濾鏈定義,從上向下順序執(zhí)行,一般將 /**放在最為下邊 //url從上向下匹配,當條件匹配成功時,就會進入指定filter并return(不會判斷后續(xù)的條件),因此這句需要在最下邊 filterChainDefinitionMap.put("/**", "authc"); //如果不設置默認會自動尋找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; } //配置核心安全事務管理器 @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("當前用戶正在訪問的url為 " + url); log.info("subject.isPermitted(url);"+subject.isPermitted(url)); //可自行根據(jù)需要判斷是否攔截,可以獲得subject判斷用戶權(quán)限,也可以使用req獲得請求頭請求體信息 //return true; return false; } //上面的方法返回false后(被攔截),會進入這個方法;這個方法返回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方法驗證該token能否登錄(使用方法2中的shiroRealm.java中的方法驗證) subject.login(usernamePasswordToken); } catch (Exception e) { //log.info("登陸失敗"); //log.info(e.getMessage()); return false; } //log.info("登陸成功"); return true; } }
5.步驟3中使用了自定義密碼驗證的方式
因此需要創(chuàng)建類CredentialsMatcher.java(與步驟3中的名稱對應),繼承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)的方式去檢驗) String inPassword = new String(utoken.getPassword()); //獲得數(shù)據(jù)庫中的密碼 String dbPassword=(String) info.getCredentials(); //進行密碼的比對 return this.equals(inPassword, dbPassword); } }
6.步驟3中放開了對登錄頁/loginController的過濾
因此我增加了一個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個字段 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"; } }
個人經(jīng)驗
1.shiro配置類中的url攔截的執(zhí)行順序為從上到下,如果url匹配到一個規(guī)則,則會跳出匹配方法,忽略后續(xù)的匹配規(guī)則(相當于return)。
2.shiro使用自定義filter時,最好繼承shiro的filter,不要直接繼承Filter類。
3.shiro使用自定義filter時,map集合的key配置為"authc"、value配置為"new MyFilter()"時,表示對配置為authc的url使用自定義filter進行攔截,而不會使用ShiroRealm中的驗證方法驗證(可能是將shiro默認的authc的攔截器覆蓋了);因此最好將key配置為其它自定義的字符串,將部分url的攔截規(guī)則設置為使用自定義filter攔截即可(如果仍想使用shiro默認的攔截器,可用逗號連接"authc")。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
使用Spirng Boot Admin監(jiān)控Spring Cloud應用項目
這篇文章主要介紹了使用Spirng Boot Admin監(jiān)控Spring Cloud應用項目,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05利用Spring Session和redis對Session進行共享詳解
這篇文章主要給大家介紹了關于利用Spring、Session和redis對Session進行共享的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2017-09-09