中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

順德專業(yè)網(wǎng)站制作友情鏈接分析

順德專業(yè)網(wǎng)站制作,友情鏈接分析,網(wǎng)站建設(shè)合同需要注意什么,網(wǎng)站做接口到app價格前言 Apache Shiro 是一個強大且容易使用的Java安全礦建,執(zhí)行身份驗證,授權(quán),密碼和會話管理。使用Shiro的易于理解的API您可以快速輕松的獲得任何應(yīng)用程序直到大的項目。 一丶什么是Shiro 1.Shiro是什么 Apache Shiro是一個強大且易于使用…

前言

Apache Shiro 是一個強大且容易使用的Java安全礦建,執(zhí)行身份驗證,授權(quán),密碼和會話管理。使用Shiro的易于理解的API您可以快速輕松的獲得任何應(yīng)用程序直到大的項目。

一丶什么是Shiro

1.Shiro是什么

Apache Shiro是一個強大且易于使用的Java安全框架,執(zhí)行身份驗證,授權(quán),密碼和會話管理。主要的好處是易于理解。

  • 驗證用戶來核實他們的身份
  • 對用戶執(zhí)行訪問控制
  • 判斷用戶是否被分配了一個確定的安全角色
  • 判斷用戶是否被允許做某事
  • 在任何環(huán)境下使用SessionAPI,即使沒有Web容器
  • 在身份驗證,訪問控制期間或在會話的生命周期,對事件做出反應(yīng)
  • 聚集一個或多個用戶安全數(shù)據(jù)的數(shù)據(jù)源,并且作為一個單一的復(fù)合用戶視圖
  • 啟動單點登錄(SSO)功能
  • 為沒有關(guān)聯(lián)到登錄的用戶做Remember Me服務(wù)

2.Shiro的功能模塊

shiro可以非常容易開發(fā)出足夠好用的應(yīng)用,其不僅可以再JaveSE環(huán)境,也可以在JavaEE環(huán)境。Shiro可以幫助我們完成:認證授權(quán)加密會話管理,與WEB集成,緩存等。也可以與springboot集成快速開發(fā)

?

  • Authentication:身份認證/登錄,驗證用戶是不是擁有相應(yīng)的身份。
  • Authorization:授權(quán),即權(quán)限驗證,驗證某個已認證的用戶是否擁有某個權(quán)限;即判斷用戶是否能做事情。
  • Session Management:會話管理,即用戶登錄后就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通JavaSE環(huán)境的,也可以是如Web環(huán)境的。
  • Cryptography:加密,保護數(shù)據(jù)的安全性,如密碼加密存儲到數(shù)據(jù)庫,而不是明文存儲。
  • Web Support:Shiro 的 web 支持的 API 能夠輕松地幫助保護 Web 應(yīng)用程序。
  • Caching:緩存,比如用戶登錄后,其用戶信息、擁有的角色/權(quán)限不必每次去查,這樣可以提高效率。
  • Concurrency:Apache Shiro 利用它的并發(fā)特性來支持多線程應(yīng)用程序。
  • Testing:測試支持的存在來幫助你編寫單元測試和集成測試,并確保你的能夠如預(yù)期的一樣安全。
  • "Run As":一個允許用戶假設(shè)為另一個用戶身份(如果允許)的功能,有時候在管理腳本很有用。
  • "Remember Me":記住我。

二丶Shiro的內(nèi)部結(jié)構(gòu)

  • ?Subject:主體,是任何可以與應(yīng)用交互的? ?“技術(shù)用戶”
  • Security Manager:相當(dāng)于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心臟;所有具體的交互都通過SecurityManager進行控制;他管理著所有的Subject,負責(zé)進行認證,授權(quán),會話,緩存和管理
  • Authenticator:認證器,負責(zé)主體認證的,這是一個擴展點,如果用具覺得默認不好,可以自定義實現(xiàn)。需要認證策略,即認證通過的情況
  • Authrizer:授權(quán)器,或者訪問控制器,用來決定主體是否有權(quán)限進行相應(yīng)的操作
  • Realm:可以有一個或多個Realm,可以認為是安全實體的數(shù)據(jù)源,即用于獲取安全實體的,可以是JDBC實現(xiàn),也可以是L:DAP實現(xiàn),或者內(nèi)存實現(xiàn)等等。由用戶提供,注意:Shiro不知道你的用戶/權(quán)限存儲在哪,以及以何種形式存儲;所有我們一般在應(yīng)用中都需要實現(xiàn)自己的Realm;
  • sessionManager:SessionManager是管理Session會話域生命周期的組件。而Shiro并不僅僅可以用在Web環(huán)境,也可以用在如普通的JavaSE環(huán)境、EJB等環(huán)境;所有呢,Shiro就抽象了一個自己的Session來管理主體與應(yīng)用之間交互的數(shù)據(jù);
  • SessionDAO:數(shù)據(jù)訪問對象,用于會話的CRUD,比如我們想把Session保存到數(shù)據(jù)庫,那么可以實現(xiàn)自己的SessionDAO,通過如JDBC寫到數(shù)據(jù)庫;比如想把Session放到Memcached中,可以實現(xiàn)自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache進行緩存,以提高性能;
  • CacheManager:緩存控制器,來管理如用戶,角色,權(quán)限等的緩存;因為這些數(shù)據(jù)基本上很少去改變,放到緩存中提高訪問性能
  • Crayptography:密碼模塊,Shiro提高了一些常見的加密組件用于如密碼的加密和解密

三丶應(yīng)用程序使用Shiro

一個簡單的Shiro應(yīng)用最少應(yīng)該如下:?

1.應(yīng)用代碼通過Subject來進行認證和授權(quán),而Subject又委托給SecurityManager;

2.我們需要給Shiro的SecurityManager注入Realm,從而讓SecurityManager能得到合法的用戶及其權(quán)限判斷

總而言之:Shiro不提供維護用戶/權(quán)限,而是通過Realm讓開發(fā)人員自己注入。?

四丶Shiro的入門

1.搭建基于ini的運行環(huán)境

創(chuàng)建工程導(dǎo)入shiro坐標(biāo)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"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>cn.xidida</groupId><artifactId>shiro_demo</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.3.2</version></dependency>
<!--        測試 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.3</version></dependency></dependencies></project>

2.用戶認證

認證:身份認證/登錄,驗證用戶是否擁有相應(yīng)的身份,基于Shiro的認證,是通過subject的login方法完成用戶認證的工作

2.1在resource目錄下創(chuàng)建愛你shiro的ini配置文件構(gòu)造模擬數(shù)據(jù)(shiro_config,ini)

2.2用戶授權(quán):

 /*** 設(shè)置角色權(quán)限** @param user 用戶信息*/public void setRolePermission(SysUser user){List<SysRole> roles = user.getRoles();if (!roles.isEmpty() && roles.size() > 1){// 多角色設(shè)置permissions屬性,以便數(shù)據(jù)權(quán)限匹配權(quán)限for (SysRole role : roles){Set<String> rolePerms = menuService.selectPermsByRoleId(role.getRoleId());role.setPermissions(rolePerms);}}}

2.3登錄權(quán)限校驗

@Component
public class SysLoginService
{@Autowired//密碼private SysPasswordService passwordService;@Autowired//用戶private ISysUserService userService;@Autowired//訪問列表private ISysMenuService menuService;@Autowired//配置private ISysConfigService configService;/*** 登錄*/public SysUser login(String username, String password){// 驗證碼校驗if (ShiroConstants.CAPTCHA_ERROR.equals(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));throw new CaptchaException();}// 用戶名或密碼為空 錯誤if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));throw new UserNotExistsException();}// 密碼如果不在指定范圍內(nèi) 錯誤if (password.length() < UserConstants.PASSWORD_MIN_LENGTH|| password.length() > UserConstants.PASSWORD_MAX_LENGTH){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}// 用戶名不在指定范圍內(nèi) 錯誤if (username.length() < UserConstants.USERNAME_MIN_LENGTH|| username.length() > UserConstants.USERNAME_MAX_LENGTH){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}// IP黑名單校驗String blackStr = configService.selectConfigByKey("sys.login.blackIPList");if (IpUtils.isMatchedIp(blackStr, ShiroUtils.getIp())){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));throw new BlackListException();}// 查詢用戶信息SysUser user = userService.selectUserByLoginName(username);if (user == null){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists")));throw new UserNotExistsException();}if (UserStatus.DELETED.getCode().equals(user.getDelFlag())){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.delete")));throw new UserDeleteException();}if (UserStatus.DISABLE.getCode().equals(user.getStatus())){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked")));throw new UserBlockedException();}passwordService.validate(user, password);AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));setRolePermission(user);recordLoginInfo(user.getUserId());return user;}

2.4注冊校驗

@Component
public class SysRegisterService
{@Autowiredprivate ISysUserService userService;@Autowiredprivate SysPasswordService passwordService;/*** 注冊*/public String register(SysUser user){String msg = "", loginName = user.getLoginName(), password = user.getPassword();if (ShiroConstants.CAPTCHA_ERROR.equals(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))){msg = "驗證碼錯誤";}else if (StringUtils.isEmpty(loginName)){msg = "用戶名不能為空";}else if (StringUtils.isEmpty(password)){msg = "用戶密碼不能為空";}else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH|| password.length() > UserConstants.PASSWORD_MAX_LENGTH){msg = "密碼長度必須在5到20個字符之間";}else if (loginName.length() < UserConstants.USERNAME_MIN_LENGTH|| loginName.length() > UserConstants.USERNAME_MAX_LENGTH){msg = "賬戶長度必須在2到20個字符之間";}else if (!userService.checkLoginNameUnique(user)){msg = "保存用戶'" + loginName + "'失敗,注冊賬號已存在";}else{user.setPwdUpdateDate(DateUtils.getNowDate());user.setUserName(loginName);user.setSalt(ShiroUtils.randomSalt());user.setPassword(passwordService.encryptPassword(loginName, password, user.getSalt()));boolean regFlag = userService.registerUser(user);if (!regFlag){msg = "注冊失敗,請聯(lián)系系統(tǒng)管理人員";}else{AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.REGISTER, MessageUtils.message("user.register.success")));}}return msg;}
}

2.5自定義Realm

Realm域:Shiro從Realm獲取安全數(shù)據(jù)(如用戶、角色、權(quán)限),就是說SecurityManager要驗證用戶身份,那么它需要從Realm獲取相應(yīng)的用戶進行比較以確定用戶身份是否合法;也需要從Realm得到用戶相應(yīng)的角色/權(quán)限進行驗證用戶是否能進行操作;可以把Realm看成DataSource,即安全數(shù)據(jù)源

/*** 自定義Realm 處理登錄 權(quán)限* * @author ruoyi*/
public class UserRealm extends AuthorizingRealm
{private static final Logger log = LoggerFactory.getLogger(UserRealm.class);@Autowiredprivate ISysMenuService menuService;@Autowiredprivate ISysRoleService roleService;@Autowiredprivate SysLoginService loginService;/*** 授權(quán)*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0){SysUser user = ShiroUtils.getSysUser();// 角色列表Set<String> roles = new HashSet<String>();// 功能列表Set<String> menus = new HashSet<String>();SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 管理員擁有所有權(quán)限if (user.isAdmin()){info.addRole("admin");info.addStringPermission("*:*:*");}else{roles = roleService.selectRoleKeys(user.getUserId());menus = menuService.selectPermsByUserId(user.getUserId());// 角色加入AuthorizationInfo認證對象info.setRoles(roles);// 權(quán)限加入AuthorizationInfo認證對象info.setStringPermissions(menus);}return info;}/*** 登錄認證*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException{UsernamePasswordToken upToken = (UsernamePasswordToken) token;String username = upToken.getUsername();String password = "";if (upToken.getPassword() != null){password = new String(upToken.getPassword());}SysUser user = null;try{user = loginService.login(username, password);}catch (CaptchaException e){throw new AuthenticationException(e.getMessage(), e);}catch (UserNotExistsException e){throw new UnknownAccountException(e.getMessage(), e);}catch (UserPasswordNotMatchException e){throw new IncorrectCredentialsException(e.getMessage(), e);}catch (UserPasswordRetryLimitExceedException e){throw new ExcessiveAttemptsException(e.getMessage(), e);}catch (UserBlockedException e){throw new LockedAccountException(e.getMessage(), e);}catch (RoleBlockedException e){throw new LockedAccountException(e.getMessage(), e);}catch (Exception e){log.info("對用戶[" + username + "]進行登錄驗證..驗證未通過{}", e.getMessage());throw new AuthenticationException(e.getMessage(), e);}SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());return info;}/*** 清理指定用戶授權(quán)信息緩存*/public void clearCachedAuthorizationInfo(Object principal){SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());this.clearCachedAuthorizationInfo(principals);}/*** 清理所有用戶授權(quán)信息緩存*/public void clearAllCachedAuthorizationInfo(){Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();if (cache != null){for (Object key : cache.keys()){cache.remove(key);}}}
}

2.6認證與授權(quán)的執(zhí)行流程分析

(1)認證流程

  • 首先調(diào)用Subject .login(token)進行登錄,其會自動委托給SuecrityManager,調(diào)用之前必須通過SecurityUtils.setSecurityManager()設(shè)置。
  • SecurityManager負責(zé)真正的身份驗證邏輯;他會委托給Authenticator進行身份驗證
  • Authenticator才是真正的身份驗證者,ShiroAPI中核心的身份認證入口,此處插入自己的實現(xiàn)
  • ?Authenticator可能會委托給相應(yīng)的AuthenticationStrategy進行多Realm身份驗證,默認ModularRealmAuthenticator會調(diào)用AuthenticationStrategy進行多Realm身份驗證,

  • Authenticator會把相應(yīng)的token傳入Realm,從Realm獲取身份驗證信息,如果沒有返回/拋出異常表示身份驗證失敗了。此處可以配置多個Realm,將按照相應(yīng)的順序及策略進行訪問。

(2)授權(quán)流程

  1. 首先調(diào)用Subject.jsPermitted/hasRole接口,其會委托給SecurityManager,而SecurityManager接著委托給Authorize
  2. Authorizer是真正的授權(quán)者,如果我們調(diào)用如isPermitted(“user:view”),其首先會通過PermissionResolver把字符串轉(zhuǎn)換成相應(yīng)的Permission實例;
  3. 在真正授權(quán)之前,其會調(diào)用相應(yīng)的Realm獲取Subject相應(yīng)的角色/權(quán)限用于匹配傳入的角色/權(quán)限;Authorize會判斷Realm的角色/權(quán)限是否和傳入的相匹配,如果有多個Realm,則會委托給MoudlarRealmAuthorizer進行循環(huán)判斷,如果匹配如isPermitted/hasRole會返回true,否則返回授權(quán)失敗

五丶Shiro在SpringBoot中的使用

?

?1.案例說明

使用Springboot構(gòu)建應(yīng)用程序,整合shiro礦建完成用戶認證與授權(quán)

1.1整合依賴

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.3.2</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.3.2</version>
</dependency>

1.2自定義登錄方法

	@RequestMapping(value="/login")public String login(String username,String password) {/*** 密碼加密:*      shiro 提供了默認的密碼加密  =》 Md5Hash*      Md5Hash():*          參數(shù)1:加密內(nèi)容*          參數(shù)2:鹽(加密混淆字符串)(密碼+混淆字符串)==>一般是隨機字符串,到時候會存入數(shù)據(jù)庫中*          參數(shù)3:加密次數(shù)**/try {// 加密password = new Md5Hash(password, username, 3).toString();// 構(gòu)造登錄令牌UsernamePasswordToken token =  new UsernamePasswordToken(username,password);//1、獲取subjectSubject subject = SecurityUtils.getSubject();// 2、調(diào)用 subject 進行登陸subject.login(token);return "登錄成功";}catch (Exception e){return "用戶名或密碼錯誤";}}

1.3自定義realm

 import cn.itcast.shiro.domain.Permission;
import cn.itcast.shiro.domain.Role;
import cn.itcast.shiro.domain.User;
import cn.itcast.shiro.service.UserService;
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.springframework.beans.factory.annotation.Autowired;import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;/*** @author YJC* @e-mail xiaochun235@qq.com* @Date 2022/12/8 20:05* @notes shiro 中自定義 realm 域*/
public class CustomReaml extends AuthorizingRealm {@Overridepublic void setName(String name) {super.setName("customReaml");// 名稱一般用類名,也可以自定義}// 進行數(shù)據(jù)庫查詢@Autowiredprivate UserService userService;/*** 授權(quán):*      操作時,判斷用戶是否具有相應(yīng)的權(quán)限*          先認證 -- 安全數(shù)據(jù)*          在授權(quán) -- 根據(jù)安全數(shù)據(jù)獲取用戶具有的所有操作權(quán)限*** @param principalCollection* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {// 1、獲取已認證的用戶數(shù)據(jù)User user = (User) principalCollection.getPrimaryPrincipal();// 獲取安全數(shù)據(jù)// 2、根據(jù)用戶數(shù)據(jù)獲取用戶的權(quán)限信息,(所有角色,所有權(quán)限)SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();Set<String> roles = new HashSet<>();Set<String> perms = new HashSet<>();for(Role role:user.getRoles()){roles.add(role.getName());// 暫時存儲 role 的名稱for (Permission permission : role.getPermissions()){perms.add(permission.getCode());}}info.setRoles(roles);info.setStringPermissions(perms);// 返回授權(quán)return info;}/*** 認證:*      參數(shù):用戶名密碼*** @param authenticationToken* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 1、獲取用戶名密碼(token)UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();String password = new String( token.getPassword() );// token.getPassword() 返回的是一個 char數(shù)組// 2、根據(jù)用戶名查詢數(shù)據(jù)庫User user = userService.findByName(username);// 3、判斷用戶是否存在或者密碼是否一致if (user!=null && user.getPassword().equals(password)){// 4、如果一致返回安全數(shù)據(jù)// 構(gòu)造方法:安全數(shù)據(jù);密碼,realm 域SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());return info;}// 5、不一致,返回 null(拋出異常)return null;}
}

1.4Shiro的配置

SecurityManager 是 Shiro 架構(gòu)的心臟,用于協(xié)調(diào)內(nèi)部的多個組件完成全部認證授權(quán)的過程。例如通過調(diào)用realm完成認證與登錄。使用基于springboot的配置方式完成SecurityManager,Realm的裝配

無redis 配置:

package cn.itcast.shiro;import cn.itcast.shiro.reaml.CustomReaml;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.mgt.SecurityManager;/*** @author YJC* @e-mail xiaochun235@qq.com* @Date 2022/12/8 20:34* @notes shiro 配置*/@Configuration
public class ShiroConfig {// 1、創(chuàng)建 realm@Beanpublic CustomReaml getRealm(){return new CustomReaml(); //CustomReaml ==》自定義 customReaml}// 2、創(chuàng)建安全管理器@Beanpublic SecurityManager securityManager(CustomReaml reaml){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(reaml);return securityManager;}// 3、配置 shiro 的過濾器工廠/***  在 web 程序中,shiro 進行權(quán)限控制全部是通過一組過濾器集合進行控制* @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){// 1、創(chuàng)建過濾器工廠ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();// 2、設(shè)置安全管理器filterFactory.setSecurityManager(securityManager);// 3、通過配置(跳轉(zhuǎn)登陸頁面,為授權(quán)跳轉(zhuǎn)的頁面)filterFactory.setLoginUrl("/longinerror");// 跳轉(zhuǎn)到url  路徑 (沒有登陸跳轉(zhuǎn))filterFactory.setUnauthorizedUrl("/autherror");//未授權(quán)的頁面 (沒有權(quán)限跳轉(zhuǎn))// 4、設(shè)置過濾器集合/*** 設(shè)置所有的過濾器:map*      key=攔截的 url 地址*      value=過濾器類型*      可以選擇有序的 map 集合==》LinkedHashMap*/Map<String,String> filterMap = new LinkedHashMap<>();//當(dāng)前請求地址可以匿名訪問filterMap.put("/user/home","anon");// swagger 訪問地址開放地址filterMap.put("/doc.htm**","anon");filterMap.put("/webjars/**","anon");filterMap.put("/swagger**","anon");filterMap.put("/v2/**","anon");// 登陸接口filterMap.put("/login","anon");// 當(dāng)前請求地址必須認證之后才可以訪問filterMap.put("/**","authc");filterFactory.setFilterChainDefinitionMap(filterMap);return filterFactory;}// 4、開啟shiro 注解的支持//配置shiro注解支持@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}}

redis配置,注:以下配置并不是本人項目配置,考慮到功能的完整性,以下配置是開源項目若依的shiro相關(guān)跑配置

package com.ruoyi.framework.config;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.commons.io.IOUtils;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.config.ConfigurationException;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.security.CipherUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.shiro.realm.UserRealm;
import com.ruoyi.framework.shiro.session.OnlineSessionDAO;
import com.ruoyi.framework.shiro.session.OnlineSessionFactory;
import com.ruoyi.framework.shiro.web.CustomShiroFilterFactoryBean;
import com.ruoyi.framework.shiro.web.filter.LogoutFilter;
import com.ruoyi.framework.shiro.web.filter.captcha.CaptchaValidateFilter;
import com.ruoyi.framework.shiro.web.filter.kickout.KickoutSessionFilter;
import com.ruoyi.framework.shiro.web.filter.online.OnlineSessionFilter;
import com.ruoyi.framework.shiro.web.filter.sync.SyncOnlineSessionFilter;
import com.ruoyi.framework.shiro.web.session.OnlineWebSessionManager;
import com.ruoyi.framework.shiro.web.session.SpringSessionValidationScheduler;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;/*** 權(quán)限配置加載* * @author ruoyi*/
@Configuration
public class ShiroConfig
{/*** Session超時時間,單位為毫秒(默認30分鐘)*/@Value("${shiro.session.expireTime}")private int expireTime;/*** 相隔多久檢查一次session的有效性,單位毫秒,默認就是10分鐘*/@Value("${shiro.session.validationInterval}")private int validationInterval;/*** 同一個用戶最大會話數(shù)*/@Value("${shiro.session.maxSession}")private int maxSession;/*** 踢出之前登錄的/之后登錄的用戶,默認踢出之前登錄的用戶*/@Value("${shiro.session.kickoutAfter}")private boolean kickoutAfter;/*** 驗證碼開關(guān)*/@Value("${shiro.user.captchaEnabled}")private boolean captchaEnabled;/*** 驗證碼類型*/@Value("${shiro.user.captchaType}")private String captchaType;/*** 設(shè)置Cookie的域名*/@Value("${shiro.cookie.domain}")private String domain;/*** 設(shè)置cookie的有效訪問路徑*/@Value("${shiro.cookie.path}")private String path;/*** 設(shè)置HttpOnly屬性*/@Value("${shiro.cookie.httpOnly}")private boolean httpOnly;/*** 設(shè)置Cookie的過期時間,秒為單位*/@Value("${shiro.cookie.maxAge}")private int maxAge;/*** 設(shè)置cipherKey密鑰*/@Value("${shiro.cookie.cipherKey}")private String cipherKey;/*** 登錄地址*/@Value("${shiro.user.loginUrl}")private String loginUrl;/*** 權(quán)限認證失敗地址*/@Value("${shiro.user.unauthorizedUrl}")private String unauthorizedUrl;/*** 是否開啟記住我功能*/@Value("${shiro.rememberMe.enabled: false}")private boolean rememberMe;/*** 緩存管理器 使用Ehcache實現(xiàn)*/@Beanpublic EhCacheManager getEhCacheManager(){net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruoyi");EhCacheManager em = new EhCacheManager();if (StringUtils.isNull(cacheManager)){em.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream()));return em;}else{em.setCacheManager(cacheManager);return em;}}/*** 返回配置文件流 避免ehcache配置文件一直被占用,無法完全銷毀項目重新部署*/protected InputStream getCacheManagerConfigFileInputStream(){String configFile = "classpath:ehcache/ehcache-shiro.xml";InputStream inputStream = null;try{inputStream = ResourceUtils.getInputStreamForPath(configFile);byte[] b = IOUtils.toByteArray(inputStream);InputStream in = new ByteArrayInputStream(b);return in;}catch (IOException e){throw new ConfigurationException("Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e);}finally{IOUtils.closeQuietly(inputStream);}}/*** 自定義Realm*/@Beanpublic UserRealm userRealm(EhCacheManager cacheManager){UserRealm userRealm = new UserRealm();userRealm.setAuthorizationCacheName(Constants.SYS_AUTH_CACHE);userRealm.setCacheManager(cacheManager);return userRealm;}/*** 自定義sessionDAO會話*/@Beanpublic OnlineSessionDAO sessionDAO(){OnlineSessionDAO sessionDAO = new OnlineSessionDAO();return sessionDAO;}/*** 自定義sessionFactory會話*/@Beanpublic OnlineSessionFactory sessionFactory(){OnlineSessionFactory sessionFactory = new OnlineSessionFactory();return sessionFactory;}/*** 會話管理器*/@Beanpublic OnlineWebSessionManager sessionManager(){OnlineWebSessionManager manager = new OnlineWebSessionManager();// 加入緩存管理器manager.setCacheManager(getEhCacheManager());// 刪除過期的sessionmanager.setDeleteInvalidSessions(true);// 設(shè)置全局session超時時間manager.setGlobalSessionTimeout(expireTime * 60 * 1000);// 去掉 JSESSIONIDmanager.setSessionIdUrlRewritingEnabled(false);// 定義要使用的無效的Session定時調(diào)度器manager.setSessionValidationScheduler(SpringUtils.getBean(SpringSessionValidationScheduler.class));// 是否定時檢查sessionmanager.setSessionValidationSchedulerEnabled(true);// 自定義SessionDaomanager.setSessionDAO(sessionDAO());// 自定義sessionFactorymanager.setSessionFactory(sessionFactory());return manager;}/*** 安全管理器*/@Beanpublic SecurityManager securityManager(UserRealm userRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 設(shè)置realm.securityManager.setRealm(userRealm);// 記住我securityManager.setRememberMeManager(rememberMe ? rememberMeManager() : null);// 注入緩存管理器;securityManager.setCacheManager(getEhCacheManager());// session管理器securityManager.setSessionManager(sessionManager());return securityManager;}/*** 退出過濾器*/public LogoutFilter logoutFilter(){LogoutFilter logoutFilter = new LogoutFilter();logoutFilter.setLoginUrl(loginUrl);return logoutFilter;}/*** Shiro過濾器配置*/@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){CustomShiroFilterFactoryBean shiroFilterFactoryBean = new CustomShiroFilterFactoryBean();// Shiro的核心安全接口,這個屬性是必須的shiroFilterFactoryBean.setSecurityManager(securityManager);// 身份認證失敗,則跳轉(zhuǎn)到登錄頁面的配置shiroFilterFactoryBean.setLoginUrl(loginUrl);// 權(quán)限認證失敗,則跳轉(zhuǎn)到指定頁面shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);// Shiro連接約束配置,即過濾鏈的定義LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();// 對靜態(tài)資源設(shè)置匿名訪問filterChainDefinitionMap.put("/favicon.ico**", "anon");filterChainDefinitionMap.put("/ruoyi.png**", "anon");filterChainDefinitionMap.put("/html/**", "anon");filterChainDefinitionMap.put("/css/**", "anon");filterChainDefinitionMap.put("/docs/**", "anon");filterChainDefinitionMap.put("/fonts/**", "anon");filterChainDefinitionMap.put("/img/**", "anon");filterChainDefinitionMap.put("/ajax/**", "anon");filterChainDefinitionMap.put("/js/**", "anon");filterChainDefinitionMap.put("/ruoyi/**", "anon");filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");// 退出 logout地址,shiro去清除sessionfilterChainDefinitionMap.put("/logout", "logout");// 不需要攔截的訪問filterChainDefinitionMap.put("/login", "anon,captchaValidate");// 注冊相關(guān)filterChainDefinitionMap.put("/register", "anon,captchaValidate");// 系統(tǒng)權(quán)限列表// filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll());Map<String, Filter> filters = new LinkedHashMap<String, Filter>();filters.put("onlineSession", onlineSessionFilter());filters.put("syncOnlineSession", syncOnlineSessionFilter());filters.put("captchaValidate", captchaValidateFilter());filters.put("kickout", kickoutSessionFilter());// 注銷成功,則跳轉(zhuǎn)到指定頁面filters.put("logout", logoutFilter());shiroFilterFactoryBean.setFilters(filters);// 所有請求需要認證filterChainDefinitionMap.put("/**", "user,kickout,onlineSession,syncOnlineSession");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}/*** 自定義在線用戶處理過濾器*/public OnlineSessionFilter onlineSessionFilter(){OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter();onlineSessionFilter.setLoginUrl(loginUrl);onlineSessionFilter.setOnlineSessionDAO(sessionDAO());return onlineSessionFilter;}/*** 自定義在線用戶同步過濾器*/public SyncOnlineSessionFilter syncOnlineSessionFilter(){SyncOnlineSessionFilter syncOnlineSessionFilter = new SyncOnlineSessionFilter();syncOnlineSessionFilter.setOnlineSessionDAO(sessionDAO());return syncOnlineSessionFilter;}/*** 自定義驗證碼過濾器*/public CaptchaValidateFilter captchaValidateFilter(){CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter();captchaValidateFilter.setCaptchaEnabled(captchaEnabled);captchaValidateFilter.setCaptchaType(captchaType);return captchaValidateFilter;}/*** cookie 屬性設(shè)置*/public SimpleCookie rememberMeCookie(){SimpleCookie cookie = new SimpleCookie("rememberMe");cookie.setDomain(domain);cookie.setPath(path);cookie.setHttpOnly(httpOnly);cookie.setMaxAge(maxAge * 24 * 60 * 60);return cookie;}/*** 記住我*/public CookieRememberMeManager rememberMeManager(){CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();cookieRememberMeManager.setCookie(rememberMeCookie());if (StringUtils.isNotEmpty(cipherKey)){cookieRememberMeManager.setCipherKey(Base64.decode(cipherKey));}else{cookieRememberMeManager.setCipherKey(CipherUtils.generateNewKey(128, "AES").getEncoded());}return cookieRememberMeManager;}/*** 同一個用戶多設(shè)備登錄限制*/public KickoutSessionFilter kickoutSessionFilter(){KickoutSessionFilter kickoutSessionFilter = new KickoutSessionFilter();kickoutSessionFilter.setCacheManager(getEhCacheManager());kickoutSessionFilter.setSessionManager(sessionManager());// 同一個用戶最大的會話數(shù),默認-1無限制;比如2的意思是同一個用戶允許最多同時兩個人登錄kickoutSessionFilter.setMaxSession(maxSession);// 是否踢出后來登錄的,默認是false;即后者登錄的用戶踢出前者登錄的用戶;踢出順序kickoutSessionFilter.setKickoutAfter(kickoutAfter);// 被踢出后重定向到的地址;kickoutSessionFilter.setKickoutUrl("/login?kickout=1");return kickoutSessionFilter;}/*** thymeleaf模板引擎和shiro框架的整合*/@Beanpublic ShiroDialect shiroDialect(){return new ShiroDialect();}/*** 開啟Shiro注解通知器*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}
}

1.5基于注解的授權(quán)

package cn.itcast.shiro;import cn.itcast.shiro.reaml.CustomReaml;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.mgt.SecurityManager;/*** @author YJC* @e-mail xiaochun235@qq.com* @Date 2022/12/8 20:34* @notes shiro 配置*/@Configuration
public class ShiroConfig {// 1、創(chuàng)建 realm@Beanpublic CustomReaml getRealm(){return new CustomReaml(); //CustomReaml ==》自定義 customReaml}// 2、創(chuàng)建安全管理器@Beanpublic SecurityManager securityManager(CustomReaml reaml){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(reaml);return securityManager;}// 3、配置 shiro 的過濾器工廠/***  在 web 程序中,shiro 進行權(quán)限控制全部是通過一組過濾器集合進行控制* @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){// 1、創(chuàng)建過濾器工廠ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();// 2、設(shè)置安全管理器filterFactory.setSecurityManager(securityManager);// 3、通過配置(跳轉(zhuǎn)登陸頁面,為授權(quán)跳轉(zhuǎn)的頁面)filterFactory.setLoginUrl("/longinerror");// 跳轉(zhuǎn)到url  路徑 (沒有登陸跳轉(zhuǎn))filterFactory.setUnauthorizedUrl("/autherror");//未授權(quán)的頁面 (沒有權(quán)限跳轉(zhuǎn))// 4、設(shè)置過濾器集合/*** 設(shè)置所有的過濾器:map*      key=攔截的 url 地址*      value=過濾器類型*      可以選擇有序的 map 集合==》LinkedHashMap*/Map<String,String> filterMap = new LinkedHashMap<>();// 不需要登錄 即可訪問//當(dāng)前請求地址可以匿名訪問filterMap.put("/user/home","anon");// 當(dāng)前請求地址可以匿名訪問  swagger 訪問地址開放地址filterMap.put("/doc.htm**","anon");filterMap.put("/webjars/**","anon");filterMap.put("/swagger**","anon");filterMap.put("/v2/**","anon");// 登陸接口filterMap.put("/login","anon");// 當(dāng)前請求地址必須認證之后才可以訪問  需要登錄才能訪問filterMap.put("/user**","authc");filterMap.put("/test","authc");// 使用 過濾器 的形式來配置 權(quán)限// 需要當(dāng)前用戶 擁有 該角色  才能訪問  role.getName()  在 realm 中的授權(quán)存入內(nèi)容的
//        filterMap.put("/user/list","roles[系統(tǒng)管理員]");// 需要有權(quán)限才能訪問// 具有 某種權(quán)限才能訪問 查詢用戶 當(dāng)有 user-find 該權(quán)限才能訪問該接口// 也可以使用 注解的形式來配置 權(quán)限  user-find ==》為 permission.getCode() 在 realm 中的授權(quán)存入內(nèi)容的
//        filterMap.put("/user/list","perms[user-list]");// 如果不具有這個權(quán)限則就跳轉(zhuǎn)到 setUnauthorizedUrl 改接口filterFactory.setFilterChainDefinitionMap(filterMap);return filterFactory;}// 4、開啟shiro 注解的支持//配置shiro注解支持@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}}

1.6controller

  /*** import org.apache.shiro.authz.annotation.RequiresRoles;* import org.apache.shiro.authz.annotation.RequiresPermissions;* 使用 shiro 注解的形式實現(xiàn)  角色  及其 權(quán)限 授權(quán)*  @RequiresPermissions() :訪問當(dāng)前方法必須具備的權(quán)限*  @RequiresRoles()   : 訪問當(dāng)前方法所具備的角色* @return* 1、過濾器:如果權(quán)限信息不匹配 則會運行改方法 setUnauthorizedUrl("/autherror");* 2、注解:如果沒有權(quán)限則會 拋出異常 :AuthorizationException* @RequiresPermissions("user-list")* @RequiresRoles("系統(tǒng)管理員")*/@RequiresPermissions("user-list")
//    @RequiresRoles()@ApiOperation("查詢用戶")@RequestMapping(value = "/user/list",method = RequestMethod.GET)public String find() {return "查詢用戶成功";}

1.7異常處理

package com.ruoyi.framework.web.exception;import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.ModelAndView;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.DemoModeException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.security.PermissionUtils;/*** 全局異常處理器* * @author ruoyi*/
@RestControllerAdvice
public class GlobalExceptionHandler
{private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** 權(quán)限校驗異常(ajax請求返回json,redirect請求跳轉(zhuǎn)頁面)*/@ExceptionHandler(AuthorizationException.class)public Object handleAuthorizationException(AuthorizationException e, HttpServletRequest request){String requestURI = request.getRequestURI();log.error("請求地址'{}',權(quán)限校驗失敗'{}'", requestURI, e.getMessage());if (ServletUtils.isAjaxRequest(request)){return AjaxResult.error(PermissionUtils.getMsg(e.getMessage()));}else{return new ModelAndView("error/unauth");}}/*** 請求方式不支持*/@ExceptionHandler(HttpRequestMethodNotSupportedException.class)public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,HttpServletRequest request){String requestURI = request.getRequestURI();log.error("請求地址'{}',不支持'{}'請求", requestURI, e.getMethod());return AjaxResult.error(e.getMessage());}/*** 攔截未知的運行時異常*/@ExceptionHandler(RuntimeException.class)public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request){String requestURI = request.getRequestURI();log.error("請求地址'{}',發(fā)生未知異常.", requestURI, e);return AjaxResult.error(e.getMessage());}/*** 系統(tǒng)異常*/@ExceptionHandler(Exception.class)public AjaxResult handleException(Exception e, HttpServletRequest request){String requestURI = request.getRequestURI();log.error("請求地址'{}',發(fā)生系統(tǒng)異常.", requestURI, e);return AjaxResult.error(e.getMessage());}/*** 業(yè)務(wù)異常*/@ExceptionHandler(ServiceException.class)public Object handleServiceException(ServiceException e, HttpServletRequest request){log.error(e.getMessage(), e);if (ServletUtils.isAjaxRequest(request)){return AjaxResult.error(e.getMessage());}else{return new ModelAndView("error/service", "errorMessage", e.getMessage());}}/*** 請求路徑中缺少必需的路徑變量*/@ExceptionHandler(MissingPathVariableException.class)public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request){String requestURI = request.getRequestURI();log.error("請求路徑中缺少必需的路徑變量'{}',發(fā)生系統(tǒng)異常.", requestURI, e);return AjaxResult.error(String.format("請求路徑中缺少必需的路徑變量[%s]", e.getVariableName()));}/*** 請求參數(shù)類型不匹配*/@ExceptionHandler(MethodArgumentTypeMismatchException.class)public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e,HttpServletRequest request){String requestURI = request.getRequestURI();log.error("請求參數(shù)類型不匹配'{}',發(fā)生系統(tǒng)異常.", requestURI, e);return AjaxResult.error(String.format("請求參數(shù)類型不匹配,參數(shù)[%s]要求類型為:'%s',但輸入值為:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue()));}/*** 自定義驗證異常*/@ExceptionHandler(BindException.class)public AjaxResult handleBindException(BindException e){log.error(e.getMessage(), e);String message = e.getAllErrors().get(0).getDefaultMessage();return AjaxResult.error(message);}/*** 演示模式異常*/@ExceptionHandler(DemoModeException.class)public AjaxResult handleDemoModeException(DemoModeException e){return AjaxResult.error("演示模式,不允許操作");}
}

六丶Shiro中會話管理

在shiro中所有的用戶的會話信息都回由Shiro來進行控制,Shiro提供的會話可以用JavaSE/JavaEE環(huán)境,不依賴于任何底層容器,可以獨立使用,是完整的會話模塊。通過Shiro的會話管理器(SessionManager)進行統(tǒng)一的會話管理

1.什么是shiro會話管理

SessionManager(會話管理器):管理所有Subject的session包括創(chuàng)建,維護,刪除,失效,驗證等工作。SessionManager是頂層組件,由SecurityManager管理shiro提供了說那個默認實現(xiàn):

1.DefaultSessionManager:用于JavaSE環(huán)境

2.ServletContainerSessionManager:用于Web環(huán)境,直接使用servlet容器的會話。

3.DefaultWebSessionManager:用于web環(huán)境,自己維護會話(相當(dāng)于直接舍棄了Servlet容器的會話管理)。

在web程序中,通過shiro的Subject.login()方法登錄成功后,用戶的認證信息實際上是保存在HttpSession中的通過如下代碼驗證。

//登錄成功后,打印所有session內(nèi)容@ApiOperation("查看session中存儲的shiro信息")@RequestMapping(value="/show",method = RequestMethod.GET)public String show(HttpSession session) {// 獲取session中所有的鍵值Enumeration<?> enumeration = session.getAttributeNames();// 遍歷enumeration中的while (enumeration.hasMoreElements()) {// 獲取session鍵值String name = enumeration.nextElement().toString();// 根據(jù)鍵值取session中的值Object value = session.getAttribute(name);// 打印結(jié)果System.out.println("<B>" + name + "</B>=" + value + "<br>/n");}return "查看session成功";}

如果是分布式分布式系統(tǒng)或者微服務(wù)架構(gòu),簡單的web session域已經(jīng)不能實現(xiàn)功能,都是通過統(tǒng)一的認證中心盡顯用戶認證。如果使用默認的會話管理,用戶信息只會保存到一臺服務(wù)器上。那么其他的服務(wù)器就要進行會話同步。

會話管理器可以指定sessionId的生成以及獲取方式。
通過sessionDao完成模擬session存入,取出等操作??

Shiro結(jié)合redis的統(tǒng)一會話管理

1.流程分析:

2. 整合redis:

<dependency><groupId>org.crazycake</groupId><artifactId>shiro-redis</artifactId><version>3.0.0</version>
</dependency>

3.?在springboot配置文件中添加redis配置

4.自定義shiro會話管理器

 
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;/*** @author YJC* @e-mail xiaochun235@qq.com* @Date 2022/12/12 22:58* @notes  自定義 Manager*/
public class CustomSessionManager  extends DefaultWebSessionManager {/*** 頭信息中具有sessionId*      請求頭:Authorization:  sessionid*** 指定sessionId的一個方法*/protected Serializable getSessionId(ServletRequest request, ServletResponse response) {//獲取請求頭中的信息String sessionId = WebUtils.toHttp(request).getHeader("Authorization");if (StringUtils.isEmpty(sessionId)){// 如果沒有攜帶,生成新的sessionreturn super.getSessionId(request,response);}else {// 返回 sessionId// 將 sessionId 放到請求頭中request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");// 當(dāng)前 sessionid 具體是什么request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId);//該sessionId是否需要驗證request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);return sessionId;}}
}

5.總配置

package cn.itcast.shiro;import cn.itcast.shiro.reaml.CustomReaml;import cn.itcast.shiro.session.CustomSessionManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.mgt.SecurityManager;/*** @author YJC* @e-mail xiaochun235@qq.com* @Date 2022/12/8 20:34* @notes shiro 配置*/@Configuration
public class ShiroConfig {// 1、創(chuàng)建 realm@Beanpublic CustomReaml getRealm(){return new CustomReaml(); //CustomReaml ==》自定義 customReaml}// 2、創(chuàng)建安全管理器@Beanpublic SecurityManager securityManager(CustomReaml reaml){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(reaml);//redis 將自定義的會話管理器注冊到安全管理器中securityManager.setSessionManager(sessionManager());// redis 將自定義的 redis 緩存管理器注冊到安全管理器中securityManager.setCacheManager(cacheManager());return securityManager;}// 3、配置 shiro 的過濾器工廠/***  在 web 程序中,shiro 進行權(quán)限控制全部是通過一組過濾器集合進行控制* @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){// 1、創(chuàng)建過濾器工廠ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();// 2、設(shè)置安全管理器filterFactory.setSecurityManager(securityManager);// 3、通過配置(跳轉(zhuǎn)登陸頁面,為授權(quán)跳轉(zhuǎn)的頁面)filterFactory.setLoginUrl("/longinerror");// 跳轉(zhuǎn)到url  路徑 (沒有登陸跳轉(zhuǎn))filterFactory.setUnauthorizedUrl("/autherror");//未授權(quán)的頁面 (沒有權(quán)限跳轉(zhuǎn))// 4、設(shè)置過濾器集合/*** 設(shè)置所有的過濾器:map*      key=攔截的 url 地址*      value=過濾器類型*      可以選擇有序的 map 集合==》LinkedHashMap*/Map<String,String> filterMap = new LinkedHashMap<>();// 不需要登錄 即可訪問//當(dāng)前請求地址可以匿名訪問filterMap.put("/user/home","anon");// 當(dāng)前請求地址可以匿名訪問  swagger 訪問地址開放地址filterMap.put("/doc.htm**","anon");filterMap.put("/webjars/**","anon");filterMap.put("/swagger**","anon");filterMap.put("/v2/**","anon");// 登陸接口filterMap.put("/login","anon");// 當(dāng)前請求地址必須認證之后才可以訪問  需要登錄才能訪問filterMap.put("/user**","authc");filterMap.put("/test","authc");// 使用 過濾器 的形式來配置 權(quán)限// 需要當(dāng)前用戶 擁有 該角色  才能訪問  role.getName()  在 realm 中的授權(quán)存入內(nèi)容的
//        filterMap.put("/user/list","roles[系統(tǒng)管理員]");// 需要有權(quán)限才能訪問// 具有 某種權(quán)限才能訪問 查詢用戶 當(dāng)有 user-find 該權(quán)限才能訪問該接口// 也可以使用 注解的形式來配置 權(quán)限  user-find ==》為 permission.getCode() 在 realm 中的授權(quán)存入內(nèi)容的
//        filterMap.put("/user/list","perms[user-list]");// 如果不具有這個權(quán)限則就跳轉(zhuǎn)到 setUnauthorizedUrl 改接口filterFactory.setFilterChainDefinitionMap(filterMap);return filterFactory;}// 獲取redis 配置@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;@Value("${spring.redis.password}")private String password;/***1、redis 配置 控制器*/public RedisManager redisManager(){RedisManager redisManager = new RedisManager();redisManager.setPort(port);redisManager.setHost(host);redisManager.setPassword(password);return redisManager;}/*** 2、redis SessionDao* @return*/public RedisSessionDAO redisSessionDAO(){RedisSessionDAO redisSessionDAO = new RedisSessionDAO();redisSessionDAO.setRedisManager(redisManager());return redisSessionDAO;}/*** 3、redis 配置自定義會話管理器* @return*/public DefaultWebSessionManager sessionManager(){CustomSessionManager customSessionManager = new CustomSessionManager();customSessionManager.setSessionDAO(redisSessionDAO());// 禁用 cookie (可寫)customSessionManager.setSessionIdCookieEnabled(false);// 禁用 url 重寫 url;jsessionid=id (可寫)customSessionManager.setSessionIdUrlRewritingEnabled(false);return customSessionManager;}/*** 4、Redis 緩存管理器* @return*/public RedisCacheManager cacheManager(){RedisCacheManager redisCacheManager = new RedisCacheManager();redisCacheManager.setRedisManager(redisManager());return redisCacheManager;}// 4、開啟shiro 注解的支持//配置shiro注解支持@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}}

http://www.risenshineclean.com/news/43454.html

相關(guān)文章:

  • 一元注冊公司流程汕頭最好的seo外包
  • 昆明網(wǎng)站制作網(wǎng)頁環(huán)球軍事新聞最新消息
  • 網(wǎng)站建設(shè)需求調(diào)研計劃表網(wǎng)絡(luò)推廣的方法和技巧
  • 義烏網(wǎng)站優(yōu)化福建seo學(xué)校
  • 網(wǎng)站做提示框今日新聞50字
  • 域名鏈接網(wǎng)站網(wǎng)絡(luò)推廣都是收費
  • 網(wǎng)站推廣文案谷歌商店下載
  • 網(wǎng)站設(shè)計建設(shè)公司seo原創(chuàng)工具
  • 沈陽男科醫(yī)院哪家好醫(yī)關(guān)于進一步優(yōu)化 廣州
  • 廣州網(wǎng)站制作系統(tǒng)優(yōu)化seo方法
  • 有免費做網(wǎng)站的嗎北京網(wǎng)站優(yōu)化seo
  • 網(wǎng)站備案屬于公司哪一塊能讓手機流暢到爆的軟件
  • 鄭州網(wǎng)站開發(fā)與建設(shè)長沙網(wǎng)站優(yōu)化seo
  • 網(wǎng)站的banner輪播怎么做網(wǎng)站秒收錄
  • 公司網(wǎng)站的建設(shè)要注意什么臨沂百度推廣的電話
  • 百度驗證網(wǎng)站seo優(yōu)化神器
  • 廣東新聞聯(lián)播吳姍姍seo服務(wù)深圳
  • 手機網(wǎng)站開發(fā)用什么語言百度不讓訪問危險網(wǎng)站怎么辦
  • 婚戀網(wǎng)站翻譯可以做嗎中國數(shù)據(jù)統(tǒng)計網(wǎng)站
  • 通化網(wǎng)站制作濟南特大最新消息
  • 邵陽市城鄉(xiāng)建設(shè)廳網(wǎng)站一鍵清理加速
  • 自己怎么做企業(yè)網(wǎng)站建設(shè)上海短視頻seo優(yōu)化網(wǎng)站
  • 燈光設(shè)計網(wǎng)站推薦軟件開發(fā)培訓(xùn)中心
  • wordpress調(diào)整上傳文件深圳seo優(yōu)化seo優(yōu)化
  • 怎么學(xué)做淘寶免費視頻網(wǎng)站湖南好搜公司seo
  • 蘇州建站免費模板成都seo技術(shù)經(jīng)理
  • c語言在線編程網(wǎng)站優(yōu)化設(shè)計三年級下冊數(shù)學(xué)答案
  • 網(wǎng)站建設(shè)的注意事項怎么做百度推廣
  • 中職網(wǎng)站建設(shè)課件公關(guān)負面處理公司
  • 南寧哪里有做開通網(wǎng)站的一般網(wǎng)站推廣要多少錢