兩個基本的概念
安全實體:系統需要保護的具體對象數據
權限:系統相關的功能操作,例如基本的CRUD
Shiro
首先Shiro較之 Spring Security,Shiro在保持強大功能的同時,還在簡單性和靈活性方面擁有巨大優勢。
Shiro是一個強大而靈活的開源安全框架,能夠非常清晰的處理認證、授權、管理會話以及密碼加密。如下是它所具有的特點:
- 易於理解的 Java Security API;
- 簡單的身份認證(登錄),支持多種數據源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
- 對角色的簡單的簽權(訪問控制),支持細粒度的簽權;
- 支持一級緩存,以提升應用程序的性能;
- 內置的基於 POJO 企業會話管理,適用於 Web 以及非 Web 的環境;
- 異構客戶端會話訪問;
- 非常簡單的加密 API;
- 不跟任何的框架或者容器捆綁,可以獨立運行。
Shiro四大核心功能:Authentication,Authorization,Cryptography,Session Management

Shiro架構
Shiro三個核心組件:Subject, SecurityManager 和 Realms.
Subject:主體,可以看到主體可以是任何可以與應用交互的 “用戶”;
Realm:域,Shiro從Realm獲取安全數據(如用戶、角色、權限),就是說SecurityManager要驗證用戶身份,那么它需要從Realm獲取相應的用戶進行比較以確定用戶身份是否合法;也需要從Realm得到用戶相應的角色/權限進行驗證用戶是否能進行操作;可以把Realm看成DataSource,即安全數據源。
兩個配置類ShiroConfig和UserRealm
1 package com.example.shirodemo.config;
2
3 import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
4 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
5 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
6 import org.springframework.beans.factory.annotation.Qualifier;
7 import org.springframework.context.annotation.Bean;
8 import org.springframework.context.annotation.Configuration;
9
10 import java.util.LinkedHashMap;
11 import java.util.Map;
12
13 /**
14 * shiro配置類
15 */
16 @Configuration
17 public class ShiroConfig {
18 /**
19 * 創建ShiroFilterFactoryBean
20 */
21 @Bean
22 public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
23 ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
24 //設置安全管理器
25 shiroFilterFactoryBean.setSecurityManager(securityManager);
26 //添加Shiro攔截器
27 /**
28 * Shiro 內置過濾器,可以實現權限相關的攔截器
29 * anon:無需認證(登錄)可以直接訪問
30 * authc:必須認證才能訪問
31 * user:如果使用rememberMe的功能才可以訪問
32 * perms:該資源得到資源權限才可以訪問
33 * role:該資源必須得到角色權限才可以訪問
34 */
35 Map<String,String> filterMap=new LinkedHashMap<>();
36 /* filterMap.put("/add","authc");
37 filterMap.put("/update","authc");*/
38 // filterMap.put("/test","anon");
39 filterMap.put("/login","anon");
40 //添加Shiro授權攔截器
41 filterMap.put("/add","perms[添加]");
42 filterMap.put("/foresee","perms[預言未來]");
43 filterMap.put("/update","perms[修改]");
44 filterMap.put("/delete","perms[刪除]");
45 //filterMap.put("/update","perms[]");
46 //filterMap.put("/delete","perms[]");
47 //filterMap.put("/getAll","perms[]");
48 filterMap.put("/*","authc");
49 //跳轉到登陸的頁面
50 shiroFilterFactoryBean.setLoginUrl("/tologin");
51 //設置未授權的頁面
52 shiroFilterFactoryBean.setUnauthorizedUrl("/unAuth");
53 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
54
55 return shiroFilterFactoryBean;
56 }
57 /**
58 * 創建DefaultWebSecurityManager
59 */
60 @Bean("securityManager")
61 public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
62 DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
63 //關聯Realm
64 securityManager.setRealm(userRealm);
65 return securityManager;
66 }
67 /**
68 * 創建Realm
69 */
70 @Bean("userRealm")
71 public UserRealm getRealm(){
72 UserRealm userRealm=new UserRealm();
73 return userRealm;
74 }
75 /**
76 * 配置shiroDialect,用於thymeleaf和shiro標簽配合使用
77 */
78 @Bean
79 public ShiroDialect getShiroDialect(){
80 ShiroDialect shiroDialect=new ShiroDialect();
81 return shiroDialect;
82 }
83 }
package com.example.shirodemo.config;
import com.example.shirodemo.bean.Permission;
import com.example.shirodemo.bean.User;
import com.example.shirodemo.service.IPermissionService;
import com.example.shirodemo.service.IUserService;
import org.apache.shiro.SecurityUtils;
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.apache.shiro.subject.Subject;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* 自定義Realm
*/
public class UserRealm extends AuthorizingRealm {
@Resource
private IUserService userService;
@Resource
private IPermissionService permissionService;
/**
* 執行授權邏輯
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("執行授權邏輯");
/**
* 給資源授權
*/
SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
//添加授權字符串
//simpleAuthorizationInfo.addStringPermission("user:add");
//--------------------認證賬號
Subject subject= SecurityUtils.getSubject();
User user=(User)subject.getPrincipal();
User user1=userService.findById(user.getId());
if(user1==null){
//用戶名不存在
return null;
}
//-------------------開始授權
List<Permission> permissions =permissionService.getPermissionByUserId(user1.getId());
for (Permission per : permissions) {
simpleAuthorizationInfo.addStringPermission(per.getName());
System.out.println("擁有權限:"+per.getName());
}
return simpleAuthorizationInfo;
}
/**
* 執行認證邏輯
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("執行認證邏輯");
/**
* 判斷ShiroRealm邏輯UsernamePasswordToken是否正確
*/
//1判斷用戶名
UsernamePasswordToken usernamePasswordToken=(UsernamePasswordToken)authenticationToken;
User user=userService.findByname(usernamePasswordToken.getUsername());
if(user==null){
//用戶名不存在
return null;
}
//判斷密碼是否正確
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
認證過程
1 /**
2 * 登錄邏輯處理
3 */
4 @RequestMapping("/login")
5 public String login(User user, Model model) {
6 /**
7 *使用shiro編寫認證操作
8 */
9 //1:獲取subject
10 Subject subject = SecurityUtils.getSubject();
11 //2:封裝用戶賬號和密碼
12 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(), user.getPassword());
13 //3:執行登錄方法
14 try {
15 subject.login(usernamePasswordToken);
16 model.addAttribute(user);
17 //登錄成功
18 //成功后跳轉到
19 //return "redirect:/test";
20 return "/test";
21 } catch (UnknownAccountException e) {
22 //e.printStackTrace();
23 //登錄失敗用戶名不存在
24 model.addAttribute("msg","用戶名不存在");
25 return "login";
26 }catch (IncorrectCredentialsException e){
27 //登錄失敗密碼錯誤
28 model.addAttribute("msg","密碼錯誤");
29 return "login";
30 }
31 }
32 }
Subject拿到用戶數據后走UserRealm 類里面的認證邏輯,授權過程比較簡單可以看你上面的代碼,
Shiro配置攔截器
//添加Shiro攔截器
27 /**
28 * Shiro 內置過濾器,可以實現權限相關的攔截器
29 * anon:無需認證(登錄)可以直接訪問
30 * authc:必須認證才能訪問
31 * user:如果使用rememberMe的功能才可以訪問
32 * perms:該資源得到資源權限才可以訪問
33 * role:該資源必須得到角色權限才可以訪問
34 */
Spring Security
除了不能脫離Spring,shiro的功能它都有。而且Spring Security對Oauth、OpenID也有支持,Shiro則需要自己手動實現。Spring Security的權限細粒度更高,畢竟Spring Security是Spring家族的。
Spring Security一般流程為:
①當用戶登錄時,前端將用戶輸入的用戶名、密碼信息傳輸到后台,后台用一個類對象將其封裝起來,通常使用的是UsernamePasswordAuthenticationToken這個類。
②程序負責驗證這個類對象。驗證方法是調用Service根據username從數據庫中取用戶信息到實體類的實例中,比較兩者的密碼,如果密碼正確就成功登陸,同時把包含着用戶的用戶名、密碼、所具有的權限等信息的類對象放到SecurityContextHolder(安全上下文容器,類似Session)中去。
③用戶訪問一個資源的時候,首先判斷是否是受限資源。如果是的話還要判斷當前是否未登錄,沒有的話就跳到登錄頁面。
④如果用戶已經登錄,訪問一個受限資源的時候,程序要根據url去數據庫中取出該資源所對應的所有可以訪問的角色,然后拿着當前用戶的所有角色一一對比,判斷用戶是否可以訪問。
注:
OAuth在"客戶端"與"服務提供商"之間,設置了一個授權層(authorization layer)。"客戶端"不能直接登錄"服務提供商",只能登錄授權層,以此將用戶與客戶端區分開來。"客戶端"登錄授權層所用的令牌(token),與用戶的密碼不同。用戶可以在登錄的時候,指定授權層令牌的權限范圍和有效期。
"客戶端"登錄授權層以后,"服務提供商"根據令牌的權限范圍和有效期,向"客戶端"開放用戶儲存的資料。
OpenID 系統的第一部分是身份驗證,即如何通過 URI 來認證用戶身份。目前的網站都是依靠用戶名和密碼來登錄認證,這就意味着大家在每個網站都需要注冊用戶名和密碼,即便你使用的是同樣的密碼。如果使用 OpenID ,你的網站地址(URI)就是你的用戶名,而你的密碼安全的存儲在一個 OpenID 服務網站上(你可以自己建立一個 OpenID 服務網站,也可以選擇一個可信任的 OpenID 服務網站來完成注冊)。
與OpenID同屬性的身份識別服務商還有ⅥeID,ClaimID,CardSpace,Rapleaf,Trufina ID Card等,其中ⅥeID通用賬戶的應用最為廣泛。
Spring Security和Shiro
相同點:
1:認證功能
2:授權功能
3:加密功能
4:會話管理
5:緩存支持
6:rememberMe功能.......
不同點:
優點:
1:Spring Security基於Spring開發,項目中如果使用Spring作為基礎,配合Spring Security做權限更加方便,而Shiro需要和Spring進行整合開發
2:Spring Security功能比Shiro更加豐富些,例如安全防護
3:Spring Security社區資源比Shiro豐富
缺點:
1:Shiro的配置和使用比較簡單,Spring Security上手復雜
2:Shiro依賴性低,不需要任何框架和容器,可以獨立運行,而Spring Security依賴於Spring容器
