Spring Boot+Shiro實現一個Http請求的Basic認證
前言
今天跟小夥伴們分享一個實戰內容,使用Spring Boot+Shiro實現一個簡單的Http認證。
場景是這樣的,我們平時的工作中可能會對外提供一些接口,如果這些接口不做一些安全認證,什麼人都可以訪問,安全性就太低瞭,所以我們的目的就是增加一個接口的認證機制,防止別人通過接口攻擊服務器。
至於Shiro是什麼,Http的Basic認證是什麼,王子就簡單介紹一下,詳細內容請自行瞭解。
Shiro是一個Java的安全框架,可以簡單實現登錄、鑒權等等的功能。
Basic認證是一種較為簡單的HTTP認證方式,客戶端通過明文(Base64編碼格式)傳輸用戶名和密碼到服務端進行認證,通常需要配合HTTPS來保證信息傳輸的安全。
實踐部分
首先說明一下測試環境。
王子已經有瞭一套集成好Shiro的Spring Boot框架,這套框架詳細代碼就不做展示瞭,我們隻來看一下測試用例。
要測試的接口代碼如下:
/** * @author liumeng */ @RestController @RequestMapping("/test") @CrossOrigin public class TestAppController extends BaseController { /** * 數據匯總 */ @GetMapping("/list") public AjaxResult test() { return success("測試接口!"); } }
使用Shiro,一定會有Shiro的攔截器配置,這部分代碼如下:
/** * Shiro過濾器配置 */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // Shiro的核心安全接口,這個屬性是必須的 shiroFilterFactoryBean.setSecurityManager(securityManager); // 身份認證失敗,則跳轉到登錄頁面的配置 shiroFilterFactoryBean.setLoginUrl(loginUrl); // 權限認證失敗,則跳轉到指定頁面 shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl); // Shiro連接約束配置,即過濾鏈的定義 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 對靜態資源設置匿名訪問 filterChainDefinitionMap.put("/favicon.ico**", "anon"); filterChainDefinitionMap.put("/lr.png**", "anon"); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/docs/**", "anon"); filterChainDefinitionMap.put("/fonts/**", "anon"); filterChainDefinitionMap.put("/img/**", "anon"); filterChainDefinitionMap.put("/ajax/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/lr/**", "anon"); filterChainDefinitionMap.put("/captcha/captchaImage**", "anon"); // 退出 logout地址,shiro去清除session filterChainDefinitionMap.put("/logout", "logout"); // 不需要攔截的訪問 filterChainDefinitionMap.put("/login", "anon,captchaValidate"); filterChainDefinitionMap.put("/ssoLogin", "anon"); // 開啟Http的Basic認證 filterChainDefinitionMap.put("/test/**", "authcBasic"); // 註冊相關 filterChainDefinitionMap.put("/register", "anon,captchaValidate"); Map<String, Filter> filters = new LinkedHashMap<String, Filter>(); filters.put("onlineSession", onlineSessionFilter()); filters.put("syncOnlineSession", syncOnlineSessionFilter()); filters.put("captchaValidate", captchaValidateFilter()); filters.put("kickout", kickoutSessionFilter()); // 註銷成功,則跳轉到指定頁面 filters.put("logout", logoutFilter()); shiroFilterFactoryBean.setFilters(filters); // 所有請求需要認證authcBasic filterChainDefinitionMap.put("/**", "user,kickout,onlineSession,syncOnlineSession"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }
這裡我們要關註的是代碼中的
filterChainDefinitionMap.put(“/test/**”, “authcBasic”);
這部分代碼,它指定瞭我們的測試接口啟動瞭Http的Basic認證,這就是我們的第一步。
做到這裡我們可以嘗試的去用瀏覽器訪問一下接口,會發現如下情況:
這就代表Basic認證已經成功開啟瞭,這個時候我們輸入系統的用戶名和密碼,你以為它就能成功訪問瞭嗎?
答案是否定的,我們隻是開啟瞭認證,但並沒有實現認證的邏輯。
王子通過閱讀部分Shiro源碼,發現每次發送請求後,都會調用ModularRealmAuthenticator這個類的doAuthenticate方法,源碼如下:
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { assertRealmsConfigured(); Collection<Realm> realms = getRealms(); if (realms.size() == 1) { return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken); } else { return doMultiRealmAuthentication(realms, authenticationToken); } }
可以看出,這個方法主要就是對Realm進行瞭管理,因為我們的系統本身已經有兩個Ream瞭,針對的是不同情況的權限驗證,所以為瞭使用起來不沖突,我們可以繼承這個類來實現我們自己的邏輯,在配置類中增加如下內容即可:
@Bean public ModularRealmAuthenticator modularRealmAuthenticator(){ //用自己重新的覆蓋 UserModularRealmAuthericator modularRealmAuthericator = new UserModularRealmAuthericator(); modularRealmAuthericator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy()); return modularRealmAuthericator; }
然後在我們自己的UserModularRealmAuthericator類中重寫doAuthenticate方法就可以瞭,這裡面的具體實現邏輯就要看你們自己的使用場景瞭。
我們可以自己新創建一個Realm來單獨校驗Basic認證的情況,或者共用之前的Realm,這部分就自由發揮瞭。
大概內容如下:
public class UserModularRealmAuthericator extends ModularRealmAuthenticator { private static final Logger logger = LoggerFactory.getLogger(UserModularRealmAuthericator.class); @Override protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { assertRealmsConfigured(); //強制轉換返回的token UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;//所有Realm Collection<Realm> realms = getRealms(); //最終選擇的Realm Collection<Realm> typeRealms = new ArrayList<>(); for(Realm realm:realms){ if(...){ //這部分是自己的邏輯判斷,過濾出想要使用的Realm typeRealms.add(realm); } } //判斷是單Realm 還是多Realm if(typeRealms.size()==1){ return doSingleRealmAuthentication(typeRealms.iterator().next(),usernamePasswordToken); }else{ return doMultiRealmAuthentication(typeRealms,usernamePasswordToken); } } }
Realm的具體實現代碼這裡就不做演示瞭,無非就是判斷用戶名密碼是否能通過校驗的邏輯。如果不清楚,可以自行瞭解Realm的實現方式。
Realm校驗實現後,Basic認證就已經實現瞭。
測試部分
接下來我們再次使用瀏覽器對接口進行測試,輸入用戶名和密碼,就會發現接口成功響應瞭。
我們來抓取一下請求情況
可以發現,Request Header中有瞭Basic認證的信息Authorization: Basic dGVzdDoxMjM0NTY=
這部分內容是這樣的,Basic為一個固定的寫法,dGVzdDoxMjM0NTY=這部分內容是userName:Password組合後的Base64編碼,所以我們隻要給第三方提供這個編碼,他們就可以通過編碼訪問我們的接口瞭。
使用PostMan測試一下
可以發現接口是可以成功訪問的。
總結
到這裡本篇文章就結束瞭,王子向大傢仔細的介紹瞭如何使用Shiro實現一個Http請求的Basic認證,是不是很簡單呢。
以上就是Spring Boot+Shiro實現一個Http請求的Basic認證的詳細內容,更多關於Spring Boot+Shiro Http請求的Basic認證的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- shiro攔截認證的全過程記錄
- spring-shiro權限控制realm實戰教程
- shrio中hashedCredentialsMatcher密碼匹配示例詳解
- springboot整合shiro多驗證登錄功能的實現(賬號密碼登錄和使用手機驗證碼登錄)
- 詳解Java springboot 整合Shiro框架