<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)登錄

          共 20813字,需瀏覽 42分鐘

           ·

          2020-08-02 20:12

          作者: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ù)庫中測(cè)試號(hào)的密碼進(jìn)行了加密,密碼皆為123456

          Maven依賴如下:

          <dependencies>
          ????????<dependency>
          ????????????<groupId>org.springframework.bootgroupId>
          ????????????<artifactId>spring-boot-starter-webartifactId>
          ????????dependency>
          ????????<dependency>
          ????????????<groupId>mysqlgroupId>
          ????????????<artifactId>mysql-connector-javaartifactId>
          ????????????<scope>runtimescope>
          ????????dependency>
          ????????
          ????????<dependency>
          ????????????<groupId>org.springframework.bootgroupId>
          ????????????<artifactId>spring-boot-starter-aopartifactId>
          ????????dependency>
          ????????
          ????????<dependency>
          ????????????<groupId>org.projectlombokgroupId>
          ????????????<artifactId>lombokartifactId>
          ????????????<optional>trueoptional>
          ????????dependency>
          ????????
          ????????<dependency>
          ????????????<groupId>org.springframework.bootgroupId>
          ????????????<artifactId>spring-boot-starter-data-redis-reactiveartifactId>
          ????????dependency>
          ????????
          ????????<dependency>
          ????????????<groupId>com.baomidougroupId>
          ????????????<artifactId>mybatis-plus-boot-starterartifactId>
          ????????????<version>3.1.0version>
          ????????dependency>
          ????????
          ????????<dependency>
          ????????????<groupId>com.alibabagroupId>
          ????????????<artifactId>druidartifactId>
          ????????????<version>1.1.6version>
          ????????dependency>
          ????????
          ????????<dependency>
          ????????????<groupId>org.apache.shirogroupId>
          ????????????<artifactId>shiro-springartifactId>
          ????????????<version>1.4.0version>
          ????????dependency>
          ????????
          ????????<dependency>
          ????????????<groupId>org.crazycakegroupId>
          ????????????<artifactId>shiro-redisartifactId>
          ????????????<version>3.1.0version>
          ????????dependency>
          ????????
          ????????<dependency>
          ????????????<groupId>org.apache.commonsgroupId>
          ????????????<artifactId>commons-lang3artifactId>
          ????????????<version>3.5version>
          ????????dependency>
          dependencies>

          配置如下:

          #?配置端口
          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ù)庫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ù)庫類型
          ??????db-type:?MYSQL
          ??configuration:
          ????#?是否開啟自動(dòng)駝峰命名規(guī)則映射:從數(shù)據(jù)庫列名到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ù)庫中查找?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


          好文章,我在看

          瀏覽 43
          點(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>
                  国产麻豆视频 | 欧美日韩黄色极品 | 日本亚洲精品秘 入口A片 | 青青操在线播放 | 五月激情婷婷影音娱乐 |