1、shiro是什么
用官方的話說Apache Shiro是一個(gè)功能強(qiáng)大且易于使用的Java安全框架,它為開發(fā)人員提供了一種直觀而全面的解決方案,用于身份驗(yàn)證,授權(quán),加密和會(huì)話管理。
首先我們先看官方給出的Shiro外部結(jié)構(gòu)圖,看shiro是如何完成工作的:

shiro主要有三大功能模塊:
Subject:
應(yīng)用代碼直接交互的對(duì)象Subject,也就是說Shiro的對(duì)外API核心就是Subject,代表了當(dāng)前的用戶,也不一定是一個(gè)具體的人,也當(dāng)前應(yīng)用交互的任何東西都是Subject,與Subject的所有交互都會(huì)委托給SecurityManager,Subject其實(shí)就是一個(gè)門面,SecurityManager才是實(shí)際的執(zhí)行者
SecurityManager:
安全管理器,即所有與安全有關(guān)的操作都會(huì)與SecurityManager交互,并且它管理著所有的Sbject,它才是Shiro的核心,負(fù)責(zé)與Shiro的其他組件進(jìn)行交互
Realms:
Shiro從Realm獲取安全數(shù)據(jù)(用戶,角色,權(quán)限),就是說SecurityManager要驗(yàn)證用戶身份,它就需要從Realm獲取相應(yīng)的用戶進(jìn)行比較,來確定用戶的身份是否合法,也需要用Realm得到用戶相應(yīng)的角色、權(quán)限,進(jìn)行驗(yà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對(duì)象
}
這三個(gè)就對(duì)應(yīng)我們剛剛說的三個(gè)模塊,然后就開始配置這三個(gè)對(duì)象,我們先從realm開始配置,realm對(duì)象需要我們自己先定義,我們先新建一個(gè)ShiroRealm類然后繼承 AuthorizingRealm,然后重寫他的方法,一個(gè)授權(quán),一個(gè)認(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對(duì)象
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
//DefaultWebSecurityManager
//創(chuàng)建realm對(duì)象
@Bean
public ShiroRealm shiroRealm(){
return new ShiroRealm();
}
}
這樣我們寫的對(duì)象就被spring托管了,接下來我們寫DefaultWebSecurityManager,因?yàn)镈efaultWebSecurityManager需要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對(duì)象
@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對(duì)象
@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: 無(wú)需認(rèn)證就可訪問
authc: 必須認(rèn)證才能訪問
user: 必須擁有 記住我功能才可訪問
perms: 擁有對(duì)某個(gè)資源的權(quán)限才能訪問
role: 擁有某個(gè)角色權(quán)限才能訪問
*/
Map<String,String>filterMap = new HashMap<>();
filterMap.put("/version/*","authc");
filterMap.put("/api/*","anon");
bean.setFilterChainDefinitionMap(filterMap);
//設(shè)置登錄請(qǐng)求
bean.setLoginUrl("/home/toLogin");
return bean;
}
這樣配置的意思是路徑/version/是需要認(rèn)證才可訪問,/api/無(wú)需認(rèn)證就可訪問,然后沒認(rèn)證的返回登錄頁(yè)面去認(rèn)證,接下來試試,在網(wǎng)頁(yè)中輸入網(wǎng)址

因?yàn)闆]認(rèn)證過直接跳轉(zhuǎn)到了登錄頁(yè)面,這樣就算成功了

4、實(shí)現(xiàn)登錄功能
現(xiàn)在我們已經(jīng)成功攔截了我們?cè)O(shè)置的路徑,然后就要實(shí)現(xiàn)登錄通過認(rèn)證,首先寫一個(gè)用戶實(shí)體類
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Admin {
private Integer id;
//用戶名稱
private String name;
//登錄帳號(hào)
private String username;
//登錄密碼
private String password;
//創(chuàng)建時(shí)間
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","帳號(hào)密碼錯(cuò)誤");
return "home/login";
}
if (!admin.getPassword().equals(password)){
model.addAttribute("error","帳號(hào)密碼錯(cuò)誤");
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";
}
這樣我們?cè)谡?qǐng)求登錄請(qǐng)求后他會(huì)去執(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ù)庫(kù)沒有該用戶return null后會(huì)拋出一個(gè)UnknownAccountException異常,成功返回AuthenticationInfo的實(shí)現(xiàn)類
這樣我們就實(shí)現(xiàn)了登錄認(rèn)證功能