手把手教你使用shiro認證,授權,權限控制,Spring boot 集成

上篇文章介紹了shiro的架構設計,實際使用中我們該怎麼使用應用到我們的項目中呢

現在手把手教你如何將shiro應用在實際項目中,首先我們遵循下面這個圖

手把手教你使用shiro認證,授權,權限控制,Spring boot 集成

構建項目

新建一個簡單的maven項目,引入相關依賴


<project> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0/<modelversion>
<groupid>com.richard.shiro.example/<groupid>
<artifactid>shiro/<artifactid>
<version>1.0-SNAPSHOT/<version>
<parent>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-parent/<artifactid>
<version>1.5.4.RELEASE/<version>
<relativepath>
/<parent>
<properties>
<project.build.sourceencoding>UTF-8/<project.build.sourceencoding>
<project.reporting.outputencoding>UTF-8/<project.reporting.outputencoding>
<java.version>1.8/<java.version>
/<properties>
<dependencies>

<dependency>
<groupid>org.apache.shiro/<groupid>
<artifactid>shiro-spring-boot-web-starter/<artifactid>
<version>1.4.0/<version>
/<dependency>
<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-web/<artifactid>
/<dependency>
<dependency>

<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-test/<artifactid>
<scope>test/<scope>
/<dependency>
/<dependencies>
<build>
<plugins>
<plugin>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-maven-plugin/<artifactid>
/<plugin>
/<plugins>
/<build>
/<project>

新建Spring Boot 應用啟動


/**
* @Description TODO
* @Author Richard
* @Date 2019/9/17 20:53
**/
@SpringBootApplication
public class ShiroExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ShiroExampleApplication.class, args);
}
}

定義realm

/**
* @Description TODO
* @Author Richard
* @Date 2019/9/17 21:11
**/
public class CustomAuthenRealm extends AuthorizingRealm {
@Autowired
private ShiroSampleDao shiroSampleDao;
/**
* 授權調用實現方法
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

System.out.println(" ======== AuthorizationInfo ==== ");
String username = (String) super.getAvailablePrincipal(principalCollection);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
Set<string> roles = shiroSampleDao.getRolesByUsername(username);
authorizationInfo.setRoles(roles);
roles.forEach(role -> {
Set<string> permissions = this.shiroSampleDao.getPermissionsByRole(role);
authorizationInfo.addStringPermissions(permissions);
});
return authorizationInfo;
}
/**
* 登錄認證 調用實現方法
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println(" AuthenticationInfo ");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
/**
*
* 在實際應用過程中,你需要再此處,做你的密碼安全性校驗,以及一些相關的業務處理
*/
String password = this.shiroSampleDao.getPasswordByUsername(username);
return new SimpleAuthenticationInfo(username, password, getName());
}
}
/<string>/<string>

shiro配置

新建shiroConfig 配置類


@Configuration
public class ShiroConfig {
@Bean
public Realm realm() {
return new CustomAuthenRealm();
}

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<string> filterChainDefinitionMap = new LinkedHashMap<string>();
// 調用login 接口不需要登錄認證
filterChainDefinitionMap.put("/sample/login", "anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
// 使用默認的 defaultWebSecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm());
return securityManager;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
}
/<string>/<string>

關於自定義的SecurityManager

上面的defaultWebSecurityManager 使用了shiro提供的默認的 SecurityManager,當然如果你的項目特殊 SecurityManager,你也可以繼承defaultWebSecurityManager 自定義自己的SecurityManager,舉個例子:

public class CustomWebSessionManager extends DefaultWebSessionManager {
public static final String LOGIN_TOKEN_KEY = "login-Token";
private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
@Override

protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String id = WebUtils.toHttp(request).getHeader(LOGIN_TOKEN_KEY);
if (!StringUtils.isEmpty(id)) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
} else {
return super.getSessionId(request, response);
}
}
}

上面代碼是由於我為了增加項目每次請求登錄驗證的安全性,需要每次請求再請求頭上帶上一個login-token,為了方便處理,定義自己的 SecurityManager

shiro攔截器與url匹配規則

上面的配置只做了簡單的url規則匹配

filterChainDefinitionMap.put("/sample/login", "anon");

下面詳細介紹攔截器與url匹配規則

url匹配規則

  1. “?”:匹配一個字符,如”/admin?”,將匹配“ /admin1”、“/admin2”,但不匹配“/admin”
  2. “*”:匹配零個或多個字符串,如“/admin*”,將匹配“ /admin”、“/admin123”,但不匹配“/admin/1”
  3. (3)“**”:匹配路徑中的零個或多個路徑,如“/admin/**”,將匹配“/admin/a”、“/admin/a/b”

shiro過濾器

手把手教你使用shiro認證,授權,權限控制,Spring boot 集成

shiro 過濾器

anon:匿名過濾器,表示通過了url配置的資源都可以訪問,例:“/statics/**=anon”表示statics目錄下所有資源都能訪問

  1. authc:基於表單的過濾器,表示通過了url配置的資源需要登錄驗證,否則跳轉到登錄,例:“/unauthor.jsp=authc”如果用戶沒有登錄訪問unauthor.jsp則直接跳轉到登錄
  2. authcBasic:Basic的身份驗證過濾器,表示通過了url配置的資源會提示身份驗證,例:“/welcom.jsp=authcBasic”訪問welcom.jsp時會彈出身份驗證框
  3. perms:權限過濾器,表示訪問通過了url配置的資源會檢查相應權限,例:“/statics/**=perms["user:add:*,user:modify:*"]“表示訪問statics目錄下的資源時只有新增和修改的權限
  4. port:端口過濾器,表示會驗證通過了url配置的資源的請求的端口號,例:“/port.jsp=port[8088]”訪問port.jsp時端口號不是8088會提示錯誤
  5. rest:restful類型過濾器,表示會對通過了url配置的資源進行restful風格檢查,例:“/welcom=rest[user:create]”表示通過restful訪問welcom資源時只有新增權限
  6. roles:角色過濾器,表示訪問通過了url配置的資源會檢查是否擁有該角色,例:“/welcom.jsp=roles[admin]”表示訪問welcom.jsp頁面時會檢查是否擁有admin角色
  7. ssl:ssl過濾器,表示通過了url配置的資源只能通過https協議訪問,例:“/welcom.jsp=ssl”表示訪問welcom.jsp頁面如果請求協議不是https會提示錯誤
  8. user:用戶過濾器,表示可以使用登錄驗證/記住我的方式訪問通過了url配置的資源,例:“/welcom.jsp=user”表示訪問welcom.jsp頁面可以通過登錄驗證或使用記住我後訪問,否則直接跳轉到登錄
  9. logout:退出攔截器,表示執行logout方法後,跳轉到通過了url配置的資源,例:“/logout.jsp=logout”表示執行了logout方法後直接跳轉到logout.jsp頁面

過濾器分類

  1. 認證過濾器:anon、authcBasic、auchc、user、logout
  2. 授權過濾器:perms、roles、ssl、rest、port

定義測試用的controller

/**
* @Description TODO
* @Author Richard
* @Date 2019/9/17 21:22
**/
@RestController
@RequestMapping("/sample")
public class SampleController {
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String login(HttpServletRequest request, String username, String password) {
System.out.println(" username : " + username);

System.out.println(" password : " + password);
System.out.println(" request query :" + request.getQueryString());
// System.out.println(" request : " + request.getPathInfo());
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token.setRememberMe(true);
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
System.out.println("登錄 用戶帳號或密碼不正確");
return null;
} catch (LockedAccountException lae) {
System.out.println("登錄 用戶帳號已鎖定不可用");
return null;
} catch (AuthenticationException ae) {
System.out.println("登錄 認證失敗");
return null;
}
return "login sucess username : " + username + " password : " + password;
}
@GetMapping("/logout")
public void logout() {
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
}
@GetMapping("/read")
@RequiresPermissions("sample:read")
public String read() {
return " I am reading...";
}
@GetMapping("/write")
@RequiresPermissions("sample:write")
public String write() {
return " I am writing...";
}
}

關於 RequiresPermissions 註解,這是應用權限控制的註解,在shiro有如下幾種註解

RequiresAuthentication:

使用該註解標註的類,實例,方法在訪問或調用時,當前Subject必須在當前session中已經過認證。

RequiresGuest:

使用該註解標註的類,實例,方法在訪問或調用時,當前Subject可以是“gust”身份,不需要經過認證或者在原先的session中存在記錄。

RequiresPermissions:

當前Subject需要擁有某些特定的權限時,才能執行被該註解標註的方法。如果當前Subject不具有這樣的權限,則方法不會被執行。

RequiresRoles:

當前Subject必須擁有所有指定的角色時,才能訪問被該註解標註的方法。如果當天Subject不同時擁有所有指定角色,則方法不會執行還會拋出AuthorizationException異常。

RequiresUser

當前Subject必須是應用的用戶,才能訪問或調用被該註解標註的類,實例,方法。

在我們權限控制中一般要使用到的是 RequiresPermissions 和 RequiresRoles

好了,經過以上步驟,一個可應用於生產的shiro集成Spring boot就搞定了,如果沒有shiro,你需要一整套的登錄認證權限控制方案,還要人力去實現,工作量大的很。

在上面的過程中各位看官有沒有思考下面這些問題:

1、shiroConfig 各個配置的實例都有什麼用,都在整個過程發揮了什麼作用?

2、login方法做了什麼操作,怎麼進行代碼調用認證?

3、shiro是怎麼進行攔截過濾的?

4、單憑几個註解就可以實現權限控制,shiro是怎麼做到?

各位看官可以說出你的答案,後面繼續剖析這些問題。

例子的代碼地址: https://github.com/Chandgaochengdong/shiro


分享到:


相關文章: