<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot 整合Shiro實(shí)現(xiàn)動(dòng)態(tài)權(quán)限加載更新+Session共享+單點(diǎn)登錄

          共 21133字,需瀏覽 43分鐘

           ·

          2020-12-06 00:00

          武哥領(lǐng)讀:

          有源碼的教程,不會(huì)的同學(xué)下載源碼,根據(jù)教程學(xué)一下哈~


          • 作者:Sans_

          • juejin.im/post/5d087d605188256de9779e64

          一. 說明

          Shiro 是一個(gè)安全框架, 項(xiàng)目中主要用它做認(rèn)證, 授權(quán), 加密, 以及用戶的會(huì)話管理, 雖然 Shiro 沒有 SpringSecurity 功能更豐富, 但是它輕量, 簡(jiǎn)單, 在項(xiàng)目中通常業(yè)務(wù)需求 Shiro 也都能勝任.

          二. 項(xiàng)目環(huán)境

          • MyBatis-Plus 版本: 3.1.0

          • SpringBoot 版本: 2.1.5

          • JDK 版本: 1.8

          • Shiro 版本: 1.4

          • Shiro-redis 插件版本: 3.1.0

          數(shù)據(jù)表 (SQL 文件在項(xiàng)目中): 數(shù)據(jù)庫(kù)中測(cè)試號(hào)的密碼進(jìn)行了加密, 密碼皆為 123456

          Maven 依賴如下:


          ????????
          ????????????org.springframework.boot
          ????????????spring-boot-starter-web
          ????????

          ????????
          ????????????mysql
          ????????????mysql-connector-java
          ????????????runtime
          ????????

          ????????
          ????????
          ????????????org.springframework.boot
          ????????????spring-boot-starter-aop
          ????????

          ????????
          ????????
          ????????????org.projectlombok
          ????????????lombok
          ????????????true
          ????????

          ????????
          ????????
          ????????????org.springframework.boot
          ????????????spring-boot-starter-data-redis-reactive
          ????????

          ????????
          ????????
          ????????????com.baomidou
          ????????????mybatis-plus-boot-starter
          ????????????3.1.0
          ????????

          ????????
          ????????
          ????????????com.alibaba
          ????????????druid
          ????????????1.1.6
          ????????

          ????????
          ????????
          ????????????org.apache.shiro
          ????????????shiro-spring
          ????????????1.4.0
          ????????

          ????????
          ????????
          ????????????org.crazycake
          ????????????shiro-redis
          ????????????3.1.0
          ????????

          ????????
          ????????
          ????????????org.apache.commons
          ????????????commons-lang3
          ????????????3.5
          ????????




          配置如下:

          #?配置端口
          server:
          ??port:?8764
          spring:
          ??#?配置數(shù)據(jù)源
          ??datasource:
          ????driver-class-name:?com.mysql.cj.jdbc.Driver
          ????url:?jdbc:mysql://localhost:3306/my_shiro?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
          ????username:?root
          ????password:?root
          ????type:?com.alibaba.druid.pool.DruidDataSource
          ??#?Redis數(shù)據(jù)源
          ??redis:
          ????host:?localhost
          ????port:?6379
          ????timeout:?6000
          ????password:?123456
          ????jedis:
          ??????pool:
          ????????max-active:?1000??#?連接池最大連接數(shù)(使用負(fù)值表示沒有限制)
          ????????max-wait:?-1??????#?連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒有限制)
          ????????max-idle:?10??????#?連接池中的最大空閑連接
          ????????min-idle:?5???????#?連接池中的最小空閑連接
          #?mybatis-plus相關(guān)配置
          mybatis-plus:
          ??#?xml掃描,多個(gè)目錄用逗號(hào)或者分號(hào)分隔(告訴?Mapper?所對(duì)應(yīng)的?XML?文件位置)
          ??mapper-locations:?classpath:mapper/*.xml
          ??#?以下配置均有默認(rèn)值,可以不設(shè)置
          ??global-config:
          ????db-config:
          ??????#主鍵類型?AUTO:"數(shù)據(jù)庫(kù)ID自增"?INPUT:"用戶輸入ID",ID_WORKER:"全局唯一ID?(數(shù)字類型唯一ID)",?UUID:"全局唯一ID?UUID";
          ??????id-type:?auto
          ??????#字段策略?IGNORED:"忽略判斷"??NOT_NULL:"非?NULL?判斷")??NOT_EMPTY:"非空判斷"
          ??????field-strategy:?NOT_EMPTY
          ??????#數(shù)據(jù)庫(kù)類型
          ??????db-type:?MYSQL
          ??configuration:
          ????#?是否開啟自動(dòng)駝峰命名規(guī)則映射:從數(shù)據(jù)庫(kù)列名到Java屬性駝峰命名的類似映射
          ????map-underscore-to-camel-case:?true
          ????#?如果查詢結(jié)果中包含空值的列,則?MyBatis?在映射的時(shí)候,不會(huì)映射這個(gè)字段
          ????call-setters-on-nulls:?true
          ????#?這個(gè)配置會(huì)將執(zhí)行的sql打印出來,在開發(fā)或測(cè)試的時(shí)候可以用
          ????log-impl:?org.apache.ibatis.logging.stdout.StdOutImpl


          二. 編寫項(xiàng)目基礎(chǔ)類

          用戶實(shí)體, Dao,Service 等在這里省略, 請(qǐng)參考源碼

          編寫 Exception 類來處理 Shiro 權(quán)限攔截異常

          創(chuàng)建 SHA256Util 加密工具

          創(chuàng)建 Spring 工具

          /**
          ?*?@Description?Spring上下文工具類
          ?*?@Author?Sans
          ?*?@CreateTime?2019/6/17?13:40
          ?*/

          @Component
          public?class?SpringUtil?implements?ApplicationContextAware?{
          ????private?static?ApplicationContext?context;
          ????/**
          ?????*?Spring在bean初始化后會(huì)判斷是不是ApplicationContextAware的子類
          ?????*?如果該類是,setApplicationContext()方法,會(huì)將容器中ApplicationContext作為參數(shù)傳入進(jìn)去
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/17?16:58
          ?????*/

          ????@Override
          ????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
          ????????context?=?applicationContext;
          ????}
          ????/**
          ?????*?通過Name返回指定的Bean
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/17?16:03
          ?????*/

          ????public?static??T?getBean(Class?beanClass)?{
          ????????return?context.getBean(beanClass);
          ????}
          }


          創(chuàng)建 Shiro 工具

          /**
          ?*?@Description?Shiro工具類
          ?*?@Author?Sans
          ?*?@CreateTime?2019/6/15?16:11
          ?*/

          public?class?ShiroUtils?{

          ????/**?私有構(gòu)造器?**/
          ????private?ShiroUtils(){}

          ????private?static?RedisSessionDAO?redisSessionDAO?=?SpringUtil.getBean(RedisSessionDAO.class);

          ????/**
          ?????*?獲取當(dāng)前用戶Session
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/17?17:03
          ?????*?@Return?SysUserEntity?用戶信息
          ?????*/
          ????public?static?Session?getSession()?{
          ????????return?SecurityUtils.getSubject().getSession();
          ????}

          ????/**
          ?????*?用戶登出
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/17?17:23
          ?????*/

          ????public?static?void?logout()?{
          ????????SecurityUtils.getSubject().logout();
          ????}

          ????/**
          ????*?獲取當(dāng)前用戶信息
          ????*?@Author?Sans
          ????*?@CreateTime?2019/6/17?17:03
          ????*?@Return?SysUserEntity?用戶信息
          ????*/

          ????public?static?SysUserEntity?getUserInfo()?{
          ??????return?(SysUserEntity)?SecurityUtils.getSubject().getPrincipal();
          ????}

          ????/**
          ?????*?刪除用戶緩存信息
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/17?13:57
          ?????*?@Param??username??用戶名稱
          ?????*?@Param??isRemoveSession?是否刪除Session
          ?????*?@Return?void
          ?????*/

          ????public?static?void?deleteCache(String?username,?boolean?isRemoveSession){
          ????????//從緩存中獲取Session
          ????????Session?session?=?null;
          ????????Collection?sessions?=?redisSessionDAO.getActiveSessions();
          ????????SysUserEntity?sysUserEntity;
          ????????Object?attribute?=?null;
          ????????for(Session?sessionInfo?:?sessions){
          ????????????//遍歷Session,找到該用戶名稱對(duì)應(yīng)的Session
          ????????????attribute?=?sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
          ????????????if?(attribute?==?null)?{
          ????????????????continue;
          ????????????}
          ????????????sysUserEntity?=?(SysUserEntity)?((SimplePrincipalCollection)?attribute).getPrimaryPrincipal();
          ????????????if?(sysUserEntity?==?null)?{
          ????????????????continue;
          ????????????}
          ????????????if?(Objects.equals(sysUserEntity.getUsername(),?username))?{
          ????????????????session=sessionInfo;
          ????????????}
          ????????}
          ????????if?(session?==?null||attribute?==?null)?{
          ????????????return;
          ????????}
          ????????//刪除session
          ????????if?(isRemoveSession)?{
          ????????????redisSessionDAO.delete(session);
          ????????}
          ????????//刪除Cache,在訪問受限接口時(shí)會(huì)重新授權(quán)
          ????????DefaultWebSecurityManager?securityManager?=?(DefaultWebSecurityManager)?SecurityUtils.getSecurityManager();
          ????????Authenticator?authc?=?securityManager.getAuthenticator();
          ????????((LogoutAware)?authc).onLogout((SimplePrincipalCollection)?attribute);
          ????}
          }


          創(chuàng)建 Shiro 的 SessionId 生成器

          三. 編寫 Shiro 核心類

          創(chuàng)建 Realm 用于授權(quán)和認(rèn)證

          /**
          ?*?@Description?Shiro權(quán)限匹配和賬號(hào)密碼匹配
          ?*?@Author?Sans
          ?*?@CreateTime?2019/6/15?11:27
          ?*/

          public?class?ShiroRealm?extends?AuthorizingRealm?{
          ????@Autowired
          ????private?SysUserService?sysUserService;
          ????@Autowired
          ????private?SysRoleService?sysRoleService;
          ????@Autowired
          ????private?SysMenuService?sysMenuService;
          ????/**
          ?????*?授權(quán)權(quán)限
          ?????*?用戶進(jìn)行權(quán)限驗(yàn)證時(shí)候Shiro會(huì)去緩存中找,如果查不到數(shù)據(jù),會(huì)執(zhí)行這個(gè)方法去查權(quán)限,并放入緩存中
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?11:44
          ?????*/

          ????@Override
          ????protected?AuthorizationInfo?doGetAuthorizationInfo(PrincipalCollection?principalCollection)?{
          ????????SimpleAuthorizationInfo?authorizationInfo?=?new?SimpleAuthorizationInfo();
          ????????SysUserEntity?sysUserEntity?=?(SysUserEntity)?principalCollection.getPrimaryPrincipal();
          ????????//獲取用戶ID
          ????????Long?userId?=sysUserEntity.getUserId();
          ????????//這里可以進(jìn)行授權(quán)和處理
          ????????Set?rolesSet?=?new?HashSet<>();
          ????????Set?permsSet?=?new?HashSet<>();
          ????????//查詢角色和權(quán)限(這里根據(jù)業(yè)務(wù)自行查詢)
          ????????List?sysRoleEntityList?=?sysRoleService.selectSysRoleByUserId(userId);
          ????????for?(SysRoleEntity?sysRoleEntity:sysRoleEntityList)?{
          ????????????rolesSet.add(sysRoleEntity.getRoleName());
          ????????????List?sysMenuEntityList?=?sysMenuService.selectSysMenuByRoleId(sysRoleEntity.getRoleId());
          ????????????for?(SysMenuEntity?sysMenuEntity?:sysMenuEntityList)?{
          ????????????????permsSet.add(sysMenuEntity.getPerms());
          ????????????}
          ????????}
          ????????//將查到的權(quán)限和角色分別傳入authorizationInfo中
          ????????authorizationInfo.setStringPermissions(permsSet);
          ????????authorizationInfo.setRoles(rolesSet);
          ????????return?authorizationInfo;
          ????}

          ????/**
          ?????*?身份認(rèn)證
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?12:36
          ?????*/

          ????@Override
          ????protected?AuthenticationInfo?doGetAuthenticationInfo(AuthenticationToken?authenticationToken)?throws?AuthenticationException?{
          ????????//獲取用戶的輸入的賬號(hào).
          ????????String?username?=?(String)?authenticationToken.getPrincipal();
          ????????//通過username從數(shù)據(jù)庫(kù)中查找?User對(duì)象,如果找到進(jìn)行驗(yàn)證
          ????????//實(shí)際項(xiàng)目中,這里可以根據(jù)實(shí)際情況做緩存,如果不做,Shiro自己也是有時(shí)間間隔機(jī)制,2分鐘內(nèi)不會(huì)重復(fù)執(zhí)行該方法
          ????????SysUserEntity?user?=?sysUserService.selectUserByName(username);
          ????????//判斷賬號(hào)是否存在
          ????????if?(user?==?null)?{
          ????????????throw?new?AuthenticationException();
          ????????}
          ????????//判斷賬號(hào)是否被凍結(jié)
          ????????if?(user.getState()==null||user.getState().equals("PROHIBIT")){
          ????????????throw?new?LockedAccountException();
          ????????}
          ????????//進(jìn)行驗(yàn)證
          ????????SimpleAuthenticationInfo?authenticationInfo?=?new?SimpleAuthenticationInfo(
          ????????????????user,??????????????????????????????????//用戶名
          ????????????????user.getPassword(),????????????????????//密碼
          ????????????????ByteSource.Util.bytes(user.getSalt()),?//設(shè)置鹽值
          ????????????????getName()
          ????????);
          ????????//驗(yàn)證成功開始踢人(清除緩存和Session)
          ????????ShiroUtils.deleteCache(username,true);
          ????????return?authenticationInfo;
          ????}
          }


          創(chuàng)建 SessionManager 類

          創(chuàng)建 ShiroConfig 配置類

          /**
          ?*?@Description?Shiro配置類
          ?*?@Author?Sans
          ?*?@CreateTime?2019/6/10?17:42
          ?*/

          @Configuration
          public?class?ShiroConfig?{

          ????private?final?String?CACHE_KEY?=?"shiro:cache:";
          ????private?final?String?SESSION_KEY?=?"shiro:session:";
          ????private?final?int?EXPIRE?=?1800;

          ????//Redis配置
          ????@Value("${spring.redis.host}")
          ????private?String?host;
          ????@Value("${spring.redis.port}")
          ????private?int?port;
          ????@Value("${spring.redis.timeout}")
          ????private?int?timeout;
          ????@Value("${spring.redis.password}")
          ????private?String?password;

          ????/**
          ?????*?開啟Shiro-aop注解支持
          ?????*?@Attention?使用代理方式所以需要開啟代碼支持
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?8:38
          ?????*/

          ????@Bean
          ????public?AuthorizationAttributeSourceAdvisor?authorizationAttributeSourceAdvisor(SecurityManager?securityManager)?{
          ????????AuthorizationAttributeSourceAdvisor?authorizationAttributeSourceAdvisor?=?new?AuthorizationAttributeSourceAdvisor();
          ????????authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
          ????????return?authorizationAttributeSourceAdvisor;
          ????}

          ????/**
          ?????*?Shiro基礎(chǔ)配置
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?8:42
          ?????*/

          ????@Bean
          ????public?ShiroFilterFactoryBean?shiroFilterFactory(SecurityManager?securityManager){
          ????????ShiroFilterFactoryBean?shiroFilterFactoryBean?=?new?ShiroFilterFactoryBean();
          ????????shiroFilterFactoryBean.setSecurityManager(securityManager);
          ????????Map?filterChainDefinitionMap?=?new?LinkedHashMap<>();
          ????????//?注意過濾器配置順序不能顛倒
          ????????//?配置過濾:不會(huì)被攔截的鏈接
          ????????filterChainDefinitionMap.put("/static/**",?"anon");
          ????????filterChainDefinitionMap.put("/userLogin/**",?"anon");
          ????????filterChainDefinitionMap.put("/**",?"authc");
          ????????//?配置shiro默認(rèn)登錄界面地址,前后端分離中登錄界面跳轉(zhuǎn)應(yīng)由前端路由控制,后臺(tái)僅返回json數(shù)據(jù)
          ????????shiroFilterFactoryBean.setLoginUrl("/userLogin/unauth");
          ????????shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
          ????????return?shiroFilterFactoryBean;
          ????}

          ????/**
          ?????*?安全管理器
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?10:34
          ?????*/

          ????@Bean
          ????public?SecurityManager?securityManager()?{
          ????????DefaultWebSecurityManager?securityManager?=?new?DefaultWebSecurityManager();
          ????????//?自定義Ssession管理
          ????????securityManager.setSessionManager(sessionManager());
          ????????//?自定義Cache實(shí)現(xiàn)
          ????????securityManager.setCacheManager(cacheManager());
          ????????//?自定義Realm驗(yàn)證
          ????????securityManager.setRealm(shiroRealm());
          ????????return?securityManager;
          ????}

          ????/**
          ?????*?身份驗(yàn)證器
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?10:37
          ?????*/

          ????@Bean
          ????public?ShiroRealm?shiroRealm()?{
          ????????ShiroRealm?shiroRealm?=?new?ShiroRealm();
          ????????shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
          ????????return?shiroRealm;
          ????}

          ????/**
          ?????*?憑證匹配器
          ?????*?將密碼校驗(yàn)交給Shiro的SimpleAuthenticationInfo進(jìn)行處理,在這里做匹配配置
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?10:48
          ?????*/

          ????@Bean
          ????public?HashedCredentialsMatcher?hashedCredentialsMatcher()?{
          ????????HashedCredentialsMatcher?shaCredentialsMatcher?=?new?HashedCredentialsMatcher();
          ????????//?散列算法:這里使用SHA256算法;
          ????????shaCredentialsMatcher.setHashAlgorithmName(SHA256Util.HASH_ALGORITHM_NAME);
          ????????//?散列的次數(shù),比如散列兩次,相當(dāng)于?md5(md5(""));
          ????????shaCredentialsMatcher.setHashIterations(SHA256Util.HASH_ITERATIONS);
          ????????return?shaCredentialsMatcher;
          ????}

          ????/**
          ?????*?配置Redis管理器
          ?????*?@Attention?使用的是shiro-redis開源插件
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?11:06
          ?????*/

          ????@Bean
          ????public?RedisManager?redisManager()?{
          ????????RedisManager?redisManager?=?new?RedisManager();
          ????????redisManager.setHost(host);
          ????????redisManager.setPort(port);
          ????????redisManager.setTimeout(timeout);
          ????????redisManager.setPassword(password);
          ????????return?redisManager;
          ????}

          ????/**
          ?????*?配置Cache管理器
          ?????*?用于往Redis存儲(chǔ)權(quán)限和角色標(biāo)識(shí)
          ?????*?@Attention?使用的是shiro-redis開源插件
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?12:37
          ?????*/

          ????@Bean
          ????public?RedisCacheManager?cacheManager()?{
          ????????RedisCacheManager?redisCacheManager?=?new?RedisCacheManager();
          ????????redisCacheManager.setRedisManager(redisManager());
          ????????redisCacheManager.setKeyPrefix(CACHE_KEY);
          ????????//?配置緩存的話要求放在session里面的實(shí)體類必須有個(gè)id標(biāo)識(shí)
          ????????redisCacheManager.setPrincipalIdFieldName("userId");
          ????????return?redisCacheManager;
          ????}

          ????/**
          ?????*?SessionID生成器
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?13:12
          ?????*/

          ????@Bean
          ????public?ShiroSessionIdGenerator?sessionIdGenerator(){
          ????????return?new?ShiroSessionIdGenerator();
          ????}

          ????/**
          ?????*?配置RedisSessionDAO
          ?????*?@Attention?使用的是shiro-redis開源插件
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?13:44
          ?????*/

          ????@Bean
          ????public?RedisSessionDAO?redisSessionDAO()?{
          ????????RedisSessionDAO?redisSessionDAO?=?new?RedisSessionDAO();
          ????????redisSessionDAO.setRedisManager(redisManager());
          ????????redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
          ????????redisSessionDAO.setKeyPrefix(SESSION_KEY);
          ????????redisSessionDAO.setExpire(expire);
          ????????return?redisSessionDAO;
          ????}

          ????/**
          ?????*?配置Session管理器
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/12?14:25
          ?????*/

          ????@Bean
          ????public?SessionManager?sessionManager()?{
          ????????ShiroSessionManager?shiroSessionManager?=?new?ShiroSessionManager();
          ????????shiroSessionManager.setSessionDAO(redisSessionDAO());
          ????????return?shiroSessionManager;
          ????}
          }


          四. 實(shí)現(xiàn)權(quán)限控制

          Shiro 可以用代碼或者注解來控制權(quán)限, 通常我們使用注解控制, 不僅簡(jiǎn)單方便, 而且更加靈活. Shiro 注解一共有五個(gè):

          一般情況下我們?cè)陧?xiàng)目中做權(quán)限控制, 使用最多的是 RequiresPermissions 和 RequiresRoles, 允許存在多個(gè)角色和權(quán)限, 默認(rèn)邏輯是 AND, 也就是同時(shí)擁有這些才可以訪問方法, 可以在注解中以參數(shù)的形式設(shè)置成 OR

          示例

          使用順序: Shiro 注解是存在順序的, 當(dāng)多個(gè)注解在一個(gè)方法上的時(shí)候, 會(huì)逐個(gè)檢查, 知道全部通過為止, 默認(rèn)攔截順序是: RequiresRoles->RequiresPermissions->RequiresAuthentication->
          RequiresUser->RequiresGuest

          示例

          創(chuàng)建 UserRoleController 角色攔截測(cè)試類

          /**
          ?*?@Description?角色測(cè)試
          ?*?@Author?Sans
          ?*?@CreateTime?2019/6/19?11:38
          ?*/

          @RestController
          @RequestMapping("/role")
          public?class?UserRoleController?{

          ????@Autowired
          ????private?SysUserService?sysUserService;
          ????@Autowired
          ????private?SysRoleService?sysRoleService;
          ????@Autowired
          ????private?SysMenuService?sysMenuService;
          ????@Autowired
          ????private?SysRoleMenuService?sysRoleMenuService;

          ????/**
          ?????*?管理員角色測(cè)試接口
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/19?10:38
          ?????*?@Return?Map?返回結(jié)果
          ?????*/

          ????@RequestMapping("/getAdminInfo")
          ????@RequiresRoles("ADMIN")
          ????public?Map?getAdminInfo(){
          ????????Map?map?=?new?HashMap<>();
          ????????map.put("code",200);
          ????????map.put("msg","這里是只有管理員角色能訪問的接口");
          ????????return?map;
          ????}

          ????/**
          ?????*?用戶角色測(cè)試接口
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/19?10:38
          ?????*?@Return?Map?返回結(jié)果
          ?????*/

          ????@RequestMapping("/getUserInfo")
          ????@RequiresRoles("USER")
          ????public?Map?getUserInfo(){
          ????????Map?map?=?new?HashMap<>();
          ????????map.put("code",200);
          ????????map.put("msg","這里是只有用戶角色能訪問的接口");
          ????????return?map;
          ????}

          ????/**
          ?????*?角色測(cè)試接口
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/19?10:38
          ?????*?@Return?Map?返回結(jié)果
          ?????*/

          ????@RequestMapping("/getRoleInfo")
          ????@RequiresRoles(value={"ADMIN","USER"},logical?=?Logical.OR)
          ????@RequiresUser
          ????public?Map?getRoleInfo(){
          ????????Map?map?=?new?HashMap<>();
          ????????map.put("code",200);
          ????????map.put("msg","這里是只要有ADMIN或者USER角色能訪問的接口");
          ????????return?map;
          ????}

          ????/**
          ?????*?登出(測(cè)試登出)
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/19?10:38
          ?????*?@Return?Map?返回結(jié)果
          ?????*/

          ????@RequestMapping("/getLogout")
          ????@RequiresUser
          ????public?Map?getLogout(){
          ????????ShiroUtils.logout();
          ????????Map?map?=?new?HashMap<>();
          ????????map.put("code",200);
          ????????map.put("msg","登出");
          ????????return?map;
          ????}
          }


          創(chuàng)建 UserMenuController 權(quán)限攔截測(cè)試類

          /**
          ?*?@Description?權(quán)限測(cè)試
          ?*?@Author?Sans
          ?*?@CreateTime?2019/6/19?11:38
          ?*/

          @RestController
          @RequestMapping("/menu")
          public?class?UserMenuController?{

          ????@Autowired
          ????private?SysUserService?sysUserService;
          ????@Autowired
          ????private?SysRoleService?sysRoleService;
          ????@Autowired
          ????private?SysMenuService?sysMenuService;
          ????@Autowired
          ????private?SysRoleMenuService?sysRoleMenuService;

          ????/**
          ?????*?獲取用戶信息集合
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/19?10:36
          ?????*?@Return?Map?返回結(jié)果
          ?????*/

          ????@RequestMapping("/getUserInfoList")
          ????@RequiresPermissions("sys:user:info")
          ????public?Map?getUserInfoList(){
          ????????Map?map?=?new?HashMap<>();
          ????????List?sysUserEntityList?=?sysUserService.list();
          ????????map.put("sysUserEntityList",sysUserEntityList);
          ????????return?map;
          ????}

          ????/**
          ?????*?獲取角色信息集合
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/19?10:37
          ?????*?@Return?Map?返回結(jié)果
          ?????*/

          ????@RequestMapping("/getRoleInfoList")
          ????@RequiresPermissions("sys:role:info")
          ????public?Map?getRoleInfoList(){
          ????????Map?map?=?new?HashMap<>();
          ????????List?sysRoleEntityList?=?sysRoleService.list();
          ????????map.put("sysRoleEntityList",sysRoleEntityList);
          ????????return?map;
          ????}

          ????/**
          ?????*?獲取權(quán)限信息集合
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/19?10:38
          ?????*?@Return?Map?返回結(jié)果
          ?????*/

          ????@RequestMapping("/getMenuInfoList")
          ????@RequiresPermissions("sys:menu:info")
          ????public?Map?getMenuInfoList(){
          ????????Map?map?=?new?HashMap<>();
          ????????List?sysMenuEntityList?=?sysMenuService.list();
          ????????map.put("sysMenuEntityList",sysMenuEntityList);
          ????????return?map;
          ????}

          ????/**
          ?????*?獲取所有數(shù)據(jù)
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/19?10:38
          ?????*?@Return?Map?返回結(jié)果
          ?????*/

          ????@RequestMapping("/getInfoAll")
          ????@RequiresPermissions("sys:info:all")
          ????public?Map?getInfoAll(){
          ????????Map?map?=?new?HashMap<>();
          ????????List?sysUserEntityList?=?sysUserService.list();
          ????????map.put("sysUserEntityList",sysUserEntityList);
          ????????List?sysRoleEntityList?=?sysRoleService.list();
          ????????map.put("sysRoleEntityList",sysRoleEntityList);
          ????????List?sysMenuEntityList?=?sysMenuService.list();
          ????????map.put("sysMenuEntityList",sysMenuEntityList);
          ????????return?map;
          ????}

          ????/**
          ?????*?添加管理員角色權(quán)限(測(cè)試動(dòng)態(tài)權(quán)限更新)
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/19?10:39
          ?????*?@Param??username?用戶ID
          ?????*?@Return?Map?返回結(jié)果
          ?????*/

          ????@RequestMapping("/addMenu")
          ????public?Map?addMenu(){
          ????????//添加管理員角色權(quán)限
          ????????SysRoleMenuEntity?sysRoleMenuEntity?=?new?SysRoleMenuEntity();
          ????????sysRoleMenuEntity.setMenuId(4L);
          ????????sysRoleMenuEntity.setRoleId(1L);
          ????????sysRoleMenuService.save(sysRoleMenuEntity);
          ????????//清除緩存
          ????????String?username?=?"admin";
          ????????ShiroUtils.deleteCache(username,false);
          ????????Map?map?=?new?HashMap<>();
          ????????map.put("code",200);
          ????????map.put("msg","權(quán)限添加成功");
          ????????return?map;
          ????}
          }


          創(chuàng)建 UserLoginController 登錄類

          /**
          ?*?@Description?用戶登錄
          ?*?@Author?Sans
          ?*?@CreateTime?2019/6/17?15:21
          ?*/

          @RestController
          @RequestMapping("/userLogin")
          public?class?UserLoginController?{

          ????@Autowired
          ????private?SysUserService?sysUserService;

          ????/**
          ?????*?登錄
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/20?9:21
          ?????*/

          ????@RequestMapping("/login")
          ????public?Map?login(@RequestBody?SysUserEntity?sysUserEntity){
          ????????Map?map?=?new?HashMap<>();
          ????????//進(jìn)行身份驗(yàn)證
          ????????try{
          ????????????//驗(yàn)證身份和登陸
          ????????????Subject?subject?=?SecurityUtils.getSubject();
          ????????????UsernamePasswordToken?token?=?new?UsernamePasswordToken(sysUserEntity.getUsername(),?sysUserEntity.getPassword());
          ????????????//驗(yàn)證成功進(jìn)行登錄操作
          ????????????subject.login(token);
          ????????}catch?(IncorrectCredentialsException?e)?{
          ????????????map.put("code",500);
          ????????????map.put("msg","用戶不存在或者密碼錯(cuò)誤");
          ????????????return?map;
          ????????}?catch?(LockedAccountException?e)?{
          ????????????map.put("code",500);
          ????????????map.put("msg","登錄失敗,該用戶已被凍結(jié)");
          ????????????return?map;
          ????????}?catch?(AuthenticationException?e)?{
          ????????????map.put("code",500);
          ????????????map.put("msg","該用戶不存在");
          ????????????return?map;
          ????????}?catch?(Exception?e)?{
          ????????????map.put("code",500);
          ????????????map.put("msg","未知異常");
          ????????????return?map;
          ????????}
          ????????map.put("code",0);
          ????????map.put("msg","登錄成功");
          ????????map.put("token",ShiroUtils.getSession().getId().toString());
          ????????return?map;
          ????}
          ????/**
          ?????*?未登錄
          ?????*?@Author?Sans
          ?????*?@CreateTime?2019/6/20?9:22
          ?????*/

          ????@RequestMapping("/unauth")
          ????public?Map?unauth(){
          ????????Map?map?=?new?HashMap<>();
          ????????map.put("code",500);
          ????????map.put("msg","未登錄");
          ????????return?map;
          ????}
          }


          五. POSTMAN 測(cè)試

          登錄成功后會(huì)返回 TOKEN, 因?yàn)槭菃吸c(diǎn)登錄, 再次登陸的話會(huì)返回新的 TOKEN, 之前 Redis 的 TOKEN 就會(huì)失效了

          當(dāng)?shù)谝淮卧L問接口后我們可以看到緩存中已經(jīng)有權(quán)限數(shù)據(jù)了, 在次訪問接口的時(shí)候, Shiro 會(huì)直接去緩存中拿取權(quán)限, 注意訪問接口時(shí)候要設(shè)置請(qǐng)求頭.

          ADMIN 這個(gè)號(hào)現(xiàn)在沒有 sys:info:all 這個(gè)權(quán)限的, 所以無法訪問 getInfoAll 接口, 我們要?jiǎng)討B(tài)分配權(quán)限后, 要清掉緩存, 在訪問接口時(shí)候, Shiro 會(huì)去重新執(zhí)行授權(quán)方法, 之后再次把權(quán)限和角色數(shù)據(jù)放入緩存中

          訪問添加權(quán)限測(cè)試接口, 因?yàn)槭菧y(cè)試, 我把增加權(quán)限的用戶 ADMIN 寫死在里面了, 權(quán)限添加后, 調(diào)用工具類清掉緩存, 我們可以發(fā)現(xiàn), Redis 中已經(jīng)沒有緩存了

          再次訪問 getInfoAll 接口, 因?yàn)榫彺嬷袥]有數(shù)據(jù), Shiro 會(huì)重新授權(quán)查詢權(quán)限, 攔截通過

          六. 項(xiàng)目源碼

          https://gitee.com/liselotte/spring-boot-shiro-demo

          https://github.com/xuyulong2017/my-java-demo

          -END-

          我是武哥,最后給大家免費(fèi)分享我寫的 10 萬字 Spring Boot 學(xué)習(xí)筆記(帶完整目錄)以及對(duì)應(yīng)的源碼。這是我之前在 CSDN 開的一門課,所以筆記非常詳細(xì)完整,我準(zhǔn)備將資料分享出來給大家免費(fèi)學(xué)習(xí),相信大家看完一定會(huì)有所收獲(下面有下載方式)。


          可以看出,我當(dāng)時(shí)備課非常詳細(xì),目錄非常完整,讀者可以手把手跟著筆記,結(jié)合源代碼來學(xué)習(xí)?,F(xiàn)在免費(fèi)分享出來,有需要的讀者可以下載學(xué)習(xí),就在我下面的公眾號(hào)Java禿頭哥里回復(fù):筆記,就行。



          如有文章對(duì)你有幫助,

          在看轉(zhuǎn)發(fā)是對(duì)我最大的支持



          關(guān)注Java禿頭哥

          只有禿頭才能更強(qiáng)


          點(diǎn)贊是最大的支持?

          瀏覽 34
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产一区二区yy精品无码毛片 | 日本大片免费观看18勿进 | 国产一区在线播放 | 中文字幕久久青青 | 91视频久久久久久 |