基於SpringBoot2的Shiro最簡配置操作(兩個文件)
基礎環境:依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.4.0</version> </dependency>
如果不是前後端分離,要實現頁面級的權限控制,則加入以下依賴就可以使用shiro的權限標簽瞭(記得在html頭部加上相應約束:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="Thymeleaf" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro" lang="en"> ): <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
Realm:認證鑒權器
package com.rz.monomer.modules.shiro; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.rz.monomer.modules.user.entity.SysUserInfo; import com.rz.monomer.modules.user.entity.SysUserRole; import com.rz.monomer.modules.user.service.SysButtonInfoService; import com.rz.monomer.modules.user.service.SysUserInfoService; import com.rz.monomer.modules.user.service.SysUserRoleService; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; 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 java.util.Set; import java.util.stream.Collectors; /** * 認證、鑒權類(必須) * * @author sunziwen * @version 1.0 * @date 2019/11/14 14:06 **/ @Slf4j public class ShiroRealm extends AuthorizingRealm { //以下三個服務是普通Dao查詢,從數據庫查詢用戶及其角色權限信息(這個類沒有自動註入,需要在下個文件中手動註入) private SysUserInfoService userInfoService; private SysButtonInfoService buttonInfoService; private SysUserRoleService userRoleService; public ShiroRealm(SysUserInfoService userInfoService, SysButtonInfoService buttonInfoService, SysUserRoleService userRoleService) { this.userInfoService = userInfoService; this.buttonInfoService = buttonInfoService; this.userRoleService = userRoleService; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { log.info("check authorization info"); SimpleAuthorizationInfo authInfo = new SimpleAuthorizationInfo(); // 獲取當前用戶 SysUserInfo userInfo = (SysUserInfo) principals.getPrimaryPrincipal(); // 查詢角色信息 Set<Long> userRoles = userRoleService.list(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userInfo.getId())) .stream() .map(SysUserRole::getRoleId) .collect(Collectors.toSet()); //角色所有權限 Set<String> perms = buttonInfoService.getPermsByRoles(userRoles); authInfo.addStringPermissions(perms); return authInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { log.info("check authentication info"); String username = (String) token.getPrincipal(); // 獲取用戶信息 SysUserInfo user = userInfoService.getOne(new LambdaQueryWrapper<SysUserInfo>().eq(SysUserInfo::getUsername, username)); if (user == null) { return null; } /*SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(654321), getName());*/ SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), getName()); return authenticationInfo; } }
WebSecurityManager:安全管理器
package com.rz.monomer.modules.shiro; import com.rz.monomer.modules.user.service.SysButtonInfoService; import com.rz.monomer.modules.user.service.SysUserInfoService; import com.rz.monomer.modules.user.service.SysUserRoleService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; import org.apache.shiro.web.filter.authc.LogoutFilter; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; import java.util.LinkedHashMap; import java.util.Map; /** * Shiro配置類(必須) * * @author sunziwen * @version 1.0 * @date 2019/11/14 14:08 **/ @Configuration @Slf4j @AllArgsConstructor public class WebSecurityManager { private SysUserInfoService userInfoService; private SysButtonInfoService buttonInfoService; private SysUserRoleService userRoleService; /** * 安全管理器 */ @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm()); return securityManager; } /** * 認證鑒權器(安全管理器的依賴) */ @Bean public ShiroRealm realm() { return new ShiroRealm(userInfoService, buttonInfoService, userRoleService); } /** * 配置攔截規則 */ @Bean public ShiroFilterFactoryBean filter(org.apache.shiro.mgt.SecurityManager securityManager) { log.info("config shiro filter"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); // 定義URL攔截鏈 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); // 允許匿名用戶訪問首頁 filterChainDefinitionMap.put("/shiro/index", "anon"); // 定義註銷路徑 filterChainDefinitionMap.put("/shiro/logout", "logout"); // 所有用戶界面都需要身份驗證,否則會跳轉到loginurl,由FormAuthenticationFilter處理 filterChainDefinitionMap.put("/shiro/user/**", "authc"); // 為login路徑定義攔截,由FormAuthenticationFilter處理 filterChainDefinitionMap.put("/shiro/login", "authc"); // 所有vip路徑要求具備vip角色權限 filterChainDefinitionMap.put("/shiro/vip/**", "roles[vip]"); // 指定loginurl 路徑 shiroFilterFactoryBean.setLoginUrl("/shiro/login"); // 登錄成功後跳轉路徑 shiroFilterFactoryBean.setSuccessUrl("/shiro/user/"); // for un authenticated shiroFilterFactoryBean.setUnauthorizedUrl("/shiro/unauth"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); // 自定義filters,可覆蓋默認的Filter列表,參考 DefaultFilter Map<String, Filter> filters = new LinkedHashMap<String, Filter>(); // 定制logout 過濾,指定註銷後跳轉到登錄頁(默認為根路徑) LogoutFilter logoutFilter = new LogoutFilter(); logoutFilter.setRedirectUrl("/shiro/login"); filters.put("logout", logoutFilter); // 定制authc 過濾,指定登錄表單參數 FormAuthenticationFilter authFilter = new FormAuthenticationFilter(); authFilter.setUsernameParam("username"); authFilter.setPasswordParam("password"); filters.put("authc", authFilter); shiroFilterFactoryBean.setFilters(filters); return shiroFilterFactoryBean; } }
Test:登錄測試
package com.rz.monomer.modules.user.controller; import com.rz.monomer.commons.utils.Md5Encrypt; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @RestController @Slf4j public class LoginController { @PostMapping("/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password) { Subject subject = SecurityUtils.getSubject(); AuthenticationToken token = new UsernamePasswordToken(username, Md5Encrypt.md5(password)); try { // 執行登錄 subject.login(token); } catch (UnknownAccountException e) { // 未知用戶 log.warn("the account {} is not found", username); return "account not found"; } catch (IncorrectCredentialsException e) { // 用戶或密碼不正確 log.warn("the account or password is not correct"); return "account or password not correct"; } return "login success"; } }
補充:SpringBoot配置Shiro時踩坑
在SpringBoot2.0整合shiro時使用@EnableAutoConfiguration的時候需要對config文件進行掃描,即使用@ComponentScan對配置進行掃描。
或者直接使用@SpringBootApplication,但是這種方法會將主方法目錄下的所有package都進行掃描影響項目效率。
Authentication failed for token submission [org.apache.shiro.authc.UsernamePasswordToken - zxc, rememberMe=false]. Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException
當出現此異常時,一般情況是用戶名密碼不匹配,或者是在配置對應的Realm時出現空值導致匹配失敗。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。
推薦閱讀:
- springBoot前後端分離項目中shiro的302跳轉問題
- Spring Boot+Shiro實現一個Http請求的Basic認證
- SpringBoot 整合 Shiro 密碼登錄的實現代碼
- SpringBoot整合Shiro實現權限控制的代碼實現
- shrio中hashedCredentialsMatcher密碼匹配示例詳解