1灯抛、shiro是什么
用官方的話說Apache Shiro是一個功能強大且易于使用的Java安全框架福荸,它為開發(fā)人員提供了一種直觀而全面的解決方案喂分,用于身份驗證,授權(quán)块蚌,加密和會話管理闰非。
首先我們先看官方給出的Shiro外部結(jié)構(gòu)圖,看shiro是如何完成工作的:
shiro主要有三大功能模塊:
Subject:
應(yīng)用代碼直接交互的對象Subject峭范,也就是說Shiro的對外API核心就是Subject财松,代表了當(dāng)前的用戶,也不一定是一個具體的人纱控,也當(dāng)前應(yīng)用交互的任何東西都是Subject辆毡,與Subject的所有交互都會委托給SecurityManager,Subject其實就是一個門面甜害,SecurityManager才是實際的執(zhí)行者
SecurityManager:
安全管理器舶掖,即所有與安全有關(guān)的操作都會與SecurityManager交互,并且它管理著所有的Sbject唾那,它才是Shiro的核心访锻,負(fù)責(zé)與Shiro的其他組件進(jìn)行交互
Realms:
Shiro從Realm獲取安全數(shù)據(jù)(用戶褪尝,角色,權(quán)限)期犬,就是說SecurityManager要驗證用戶身份河哑,它就需要從Realm獲取相應(yīng)的用戶進(jìn)行比較,來確定用戶的身份是否合法龟虎,也需要用Realm得到用戶相應(yīng)的角色璃谨、權(quán)限,進(jìn)行驗證用戶的操作是否能夠進(jìn)行
2鲤妥、集成Shiro
1佳吞、導(dǎo)入jar包
在pom文件中添加shiro依賴
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>
2、編寫配置類
創(chuàng)建ShiroConfig類
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
//DefaultWebSecurityManager
//創(chuàng)建realm對象
}
這三個就對應(yīng)我們剛剛說的三個模塊棉安,然后就開始配置這三個對象底扳,我們先從realm開始配置,realm對象需要我們自己先定義贡耽,我們先新建一個ShiroRealm類然后繼承 AuthorizingRealm衷模,然后重寫他的方法,一個授權(quán)蒲赂,一個認(rèn)證
//自定義realm 需要繼承 AuthorizingRealm
public class ShiroRealm extends AuthorizingRealm {
//授權(quán)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//認(rèn)證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
然后返回ShiroConfig去創(chuàng)建realm對象
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
//DefaultWebSecurityManager
//創(chuàng)建realm對象
@Bean
public ShiroRealm shiroRealm(){
return new ShiroRealm();
}
}
這樣我們寫的對象就被spring托管了阱冶,接下來我們寫DefaultWebSecurityManager,因為DefaultWebSecurityManager需要Realm
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//關(guān)聯(lián)realm
securityManager.setRealm(shiroRealm);
return securityManager;
}
//創(chuàng)建realm對象
@Bean
public ShiroRealm shiroRealm(){
return new ShiroRealm();
}
}
接下來寫ShiroFilterFactoryBean
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager manager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//設(shè)置安全管理器
bean.setSecurityManager(manager);
return bean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//關(guān)聯(lián)realm
securityManager.setRealm(shiroRealm);
return securityManager;
}
//創(chuàng)建realm對象
@Bean
public ShiroRealm shiroRealm(){
return new ShiroRealm();
}
}
3滥嘴、設(shè)置過濾器
前面我們已經(jīng)把shiro初步集成了木蹬,接下來就需要配置過濾器
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager manager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//設(shè)置安全管理器
bean.setSecurityManager(manager);
/*
添加shiro的內(nèi)置過濾器
anon: 無需認(rèn)證就可訪問
authc: 必須認(rèn)證才能訪問
user: 必須擁有 記住我功能才可訪問
perms: 擁有對某個資源的權(quán)限才能訪問
role: 擁有某個角色權(quán)限才能訪問
*/
Map<String,String>filterMap = new HashMap<>();
filterMap.put("/version/*","authc");
filterMap.put("/api/*","anon");
bean.setFilterChainDefinitionMap(filterMap);
//設(shè)置登錄請求
bean.setLoginUrl("/home/toLogin");
return bean;
}
這樣配置的意思是路徑/version/是需要認(rèn)證才可訪問,/api/無需認(rèn)證就可訪問若皱,然后沒認(rèn)證的返回登錄頁面去認(rèn)證,接下來試試镊叁,在網(wǎng)頁中輸入網(wǎng)址
因為沒認(rèn)證過直接跳轉(zhuǎn)到了登錄頁面,這樣就算成功了
4走触、實現(xiàn)登錄功能
現(xiàn)在我們已經(jīng)成功攔截了我們設(shè)置的路徑意系,然后就要實現(xiàn)登錄通過認(rèn)證,首先寫一個用戶實體類
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Admin {
private Integer id;
//用戶名稱
private String name;
//登錄帳號
private String username;
//登錄密碼
private String password;
//創(chuàng)建時間
private Date createTime;
}
然后寫登錄方法
@PostMapping("login")
public String login(String adminName, String password, Model model) {
Admin admin = adminMapper.searchByAccount(adminName);
if (admin == null){
model.addAttribute("error","帳號密碼錯誤");
return "home/login";
}
if (!admin.getPassword().equals(password)){
model.addAttribute("error","帳號密碼錯誤");
return "home/login";
}
//獲取當(dāng)前用戶
Subject subject = SecurityUtils.getSubject();
//封裝用戶的登錄數(shù)據(jù)
UsernamePasswordToken token = new UsernamePasswordToken(admin.getUsername(),admin.getPassword());
try {
subject.login(token); //執(zhí)行登錄方法
return "redirect:/version/list";
} catch (Exception e){
e.printStackTrace();
}
return "home/login";
}
這樣我們在請求登錄請求后他會去執(zhí)行doGetAuthenticationInfo方法做認(rèn)證操作饺汹,那我們就去里面寫認(rèn)證操作
//認(rèn)證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String adminName = token.getUsername();
if (adminName != null && !adminName.equals("")){
Admin admin = adminMapper.searchByAccount(adminName);
if (admin != null){
return new SimpleAuthenticationInfo(admin.getUsername(),admin.getPassword(),getName());
}
}
return null;
}
如果查詢數(shù)據(jù)庫沒有該用戶return null后會拋出一個UnknownAccountException異常蛔添,成功返回AuthenticationInfo的實現(xiàn)類
這樣我們就實現(xiàn)了登錄認(rèn)證功能