Spring Boot+Shiro實現(xiàn)一個Http請求的Basic認證
前言
今天跟小伙伴們分享一個實戰(zhàn)內(nèi)容,使用Spring Boot+Shiro實現(xiàn)一個簡單的Http認證。
場景是這樣的,我們平時的工作中可能會對外提供一些接口,如果這些接口不做一些安全認證,什么人都可以訪問,安全性就太低了,所以我們的目的就是增加一個接口的認證機制,防止別人通過接口攻擊服務(wù)器。
至于Shiro是什么,Http的Basic認證是什么,王子就簡單介紹一下,詳細內(nèi)容請自行了解。
Shiro是一個Java的安全框架,可以簡單實現(xiàn)登錄、鑒權(quán)等等的功能。
Basic認證是一種較為簡單的HTTP認證方式,客戶端通過明文(Base64編碼格式)傳輸用戶名和密碼到服務(wù)端進行認證,通常需要配合HTTPS來保證信息傳輸?shù)陌踩?/p>
實踐部分
首先說明一下測試環(huán)境。
王子已經(jīng)有了一套集成好Shiro的Spring Boot框架,這套框架詳細代碼就不做展示了,我們只來看一下測試用例。
要測試的接口代碼如下:
/**
* @author liumeng
*/
@RestController
@RequestMapping("/test")
@CrossOrigin
public class TestAppController extends BaseController {
/**
* 數(shù)據(jù)匯總
*/
@GetMapping("/list")
public AjaxResult test()
{
return success("測試接口!");
}
}
使用Shiro,一定會有Shiro的攔截器配置,這部分代碼如下:
/**
* Shiro過濾器配置
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// Shiro的核心安全接口,這個屬性是必須的
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 身份認證失敗,則跳轉(zhuǎn)到登錄頁面的配置
shiroFilterFactoryBean.setLoginUrl(loginUrl);
// 權(quán)限認證失敗,則跳轉(zhuǎn)到指定頁面
shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
// Shiro連接約束配置,即過濾鏈的定義
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 對靜態(tài)資源設(shè)置匿名訪問
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");
// 注冊相關(guān)
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());
// 注銷成功,則跳轉(zhuǎn)到指定頁面
filters.put("logout", logoutFilter());
shiroFilterFactoryBean.setFilters(filters);
// 所有請求需要認證authcBasic
filterChainDefinitionMap.put("/**", "user,kickout,onlineSession,syncOnlineSession");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
這里我們要關(guān)注的是代碼中的
filterChainDefinitionMap.put("/test/**", "authcBasic");
這部分代碼,它指定了我們的測試接口啟動了Http的Basic認證,這就是我們的第一步。
做到這里我們可以嘗試的去用瀏覽器訪問一下接口,會發(fā)現(xiàn)如下情況:

這就代表Basic認證已經(jīng)成功開啟了,這個時候我們輸入系統(tǒng)的用戶名和密碼,你以為它就能成功訪問了嗎?
答案是否定的,我們只是開啟了認證,但并沒有實現(xiàn)認證的邏輯。
王子通過閱讀部分Shiro源碼,發(fā)現(xiàn)每次發(fā)送請求后,都會調(diào)用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進行了管理,因為我們的系統(tǒng)本身已經(jīng)有兩個Ream了,針對的是不同情況的權(quán)限驗證,所以為了使用起來不沖突,我們可以繼承這個類來實現(xiàn)我們自己的邏輯,在配置類中增加如下內(nèi)容即可:
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator(){
//用自己重新的覆蓋
UserModularRealmAuthericator modularRealmAuthericator = new UserModularRealmAuthericator();
modularRealmAuthericator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return modularRealmAuthericator;
}
然后在我們自己的UserModularRealmAuthericator類中重寫doAuthenticate方法就可以了,這里面的具體實現(xiàn)邏輯就要看你們自己的使用場景了。
我們可以自己新創(chuàng)建一個Realm來單獨校驗Basic認證的情況,或者共用之前的Realm,這部分就自由發(fā)揮了。
大概內(nèi)容如下:
public class UserModularRealmAuthericator extends ModularRealmAuthenticator {
private static final Logger logger = LoggerFactory.getLogger(UserModularRealmAuthericator.class);
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
//強制轉(zhuǎn)換返回的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的具體實現(xiàn)代碼這里就不做演示了,無非就是判斷用戶名密碼是否能通過校驗的邏輯。如果不清楚,可以自行了解Realm的實現(xiàn)方式。
Realm校驗實現(xiàn)后,Basic認證就已經(jīng)實現(xiàn)了。
測試部分
接下來我們再次使用瀏覽器對接口進行測試,輸入用戶名和密碼,就會發(fā)現(xiàn)接口成功響應(yīng)了。
我們來抓取一下請求情況

可以發(fā)現(xiàn),Request Header中有了Basic認證的信息Authorization: Basic dGVzdDoxMjM0NTY=
這部分內(nèi)容是這樣的,Basic為一個固定的寫法,dGVzdDoxMjM0NTY=這部分內(nèi)容是userName:Password組合后的Base64編碼,所以我們只要給第三方提供這個編碼,他們就可以通過編碼訪問我們的接口了。
使用PostMan測試一下

可以發(fā)現(xiàn)接口是可以成功訪問的。
總結(jié)
到這里本篇文章就結(jié)束了,王子向大家仔細的介紹了如何使用Shiro實現(xiàn)一個Http請求的Basic認證,是不是很簡單呢。
以上就是Spring Boot+Shiro實現(xiàn)一個Http請求的Basic認證的詳細內(nèi)容,更多關(guān)于Spring Boot+Shiro Http請求的Basic認證的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java中的Set接口實現(xiàn)類HashSet和LinkedHashSet詳解
這篇文章主要介紹了Java中的Set接口實現(xiàn)類HashSet和LinkedHashSet詳解,Set接口和java.util.List接口一樣,同樣繼承自Collection接口,它與Collection接口中的方法基本一致,并沒有對Collection接口進行功能上的擴充,只是比Collection接口更加嚴格了,需要的朋友可以參考下2024-01-01
如何解決Spring in action @valid驗證不生效的問題
這篇文章主要介紹了如何解決Spring in action @valid驗證不生效的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06

