Spring Boot集成Shiro權限管理

前言:

簡述:權限管理分爲認證和授權兩大部分

認證即爲登錄認證,授權即爲訪問某API時是否有權限訪問

 

                                       原理圖解

 

  • Suject:前臺傳值過來的對象
  • SecurityManager:權限管理核心對象
  • Authenticator:權限認證對象
  • Authorizer:權限授權對象
  • SessionManager:session管理對象
  • Realm:與數據庫連接的橋樑對象
  • ShiroUtils:此工具類可以連接以上的對象

 

一、數據表建立

 

數據表:用戶表、用戶角色中間表、角色表、角色權限中間表、權限表

/*
Navicat MySQL Data Transfer

Source Server         : localhost
Source Server Version : 50721
Source Host           : localhost:3306
Source Database       : shiro

Target Server Type    : MYSQL
Target Server Version : 50721
File Encoding         : 65001

Date: 2019-07-15 20:26:21
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `sys_perms`
-- ----------------------------
DROP TABLE IF EXISTS `sys_perms`;
CREATE TABLE `sys_perms` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `perms` varchar(255) DEFAULT NULL COMMENT '權限',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='權限表';

-- ----------------------------
-- Records of sys_perms
-- ----------------------------
INSERT INTO `sys_perms` VALUES ('1', 'sys:user:list,sys:user:add');

-- ----------------------------
-- Table structure for `sys_role`
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `role_name` varchar(64) DEFAULT NULL COMMENT '角色',
  `role_remark` varchar(64) DEFAULT NULL COMMENT '角色備註',
  `create_user_id` int(11) DEFAULT NULL COMMENT '創建人ID',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='角色表';

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'admin', '系統管理員', '1', '2019-07-14 23:37:51');

-- ----------------------------
-- Table structure for `sys_role_perms`
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_perms`;
CREATE TABLE `sys_role_perms` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `role_id` int(11) NOT NULL COMMENT '角色表外鍵',
  `perms_id` int(11) NOT NULL COMMENT '權限表外鍵',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='角色和權限中間表';

-- ----------------------------
-- Records of sys_role_perms
-- ----------------------------
INSERT INTO `sys_role_perms` VALUES ('1', '1', '1');

-- ----------------------------
-- Table structure for `sys_user`
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `username` varchar(64) DEFAULT NULL COMMENT '用戶名',
  `password` varchar(255) DEFAULT NULL COMMENT '密碼(密文)',
  `salt` varchar(255) DEFAULT NULL COMMENT '鹽',
  `create_user_id` int(11) DEFAULT NULL COMMENT '創建人ID',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='用戶表';

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', 'a7c525e97cb55128257469230e990f23', 'f6281f61-ae3d-4b4c-8650-d73f4bf01208', '1', '2019-07-10 23:41:27');
INSERT INTO `sys_user` VALUES ('2', 'justin', '2e7446f43341d2f077beafdd02641771', '47b3aec6-14af-4bd4-ae7a-45168c1a40ff', null, '2019-07-15 09:36:46');

-- ----------------------------
-- Table structure for `sys_user_role`
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `user_id` int(11) NOT NULL COMMENT '用戶表外鍵',
  `role_id` int(11) NOT NULL COMMENT '角色表外鍵',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用戶和角色中間表';

-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1', '1');

 

二 、項目搭建

 

1、項目目錄結構:

 

 

2、添加依賴,主要是shiro依賴,而swagger-ui用於測試接口

 

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>2.3.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


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

        <!--swagger2-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>
    </dependencies>

 

3、配置yml文件

 

#配置mysql
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/shiro?serverTimezone=GMT
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
#配置mybatis-plus
mybatis-plus:
  mapper-locations: classpath:/mybatis/mapper/*.xml
#顯示sql語句
logging:
  level:
    cn.zdxh.shirodemo.mapper: DEBUG

 

4、首先定義一個統一返回json的包裝類

 

Result

package cn.zdxh.shirodemo.utils;

import lombok.Data;

@Data
public class Result<T> {
    private Integer code;
    private String msg;
    private T data;

    public static <T> Result success(T t){
        Result<T> result = new Result();
        result.setCode(1);
        result.setMsg("成功");
        result.setData(t);
        return result;
    }

    public static <T> Result error(T t){
        Result result = new Result();
        result.setCode(0);
        result.setMsg("失敗");
        result.setData(t);
        return result;
    }
}

 

5、Shiro加密工具類

 

ShiroUtils

package cn.zdxh.shirodemo.utils;

import org.apache.shiro.crypto.hash.Md5Hash;

public class ShiroUtils {

    //密碼加密
    public static String encryptMD5(String password){
        return new Md5Hash(password).toString();
    }

    //通過MD5加鹽加密
    public static String encryptMD5(String password,String salt){
        return new Md5Hash(password,salt).toString();
    }
}

 

6、定義實體類

 

  • 這裏只給出一個SysUser實體類,其他實體類的同理。
  • 需要注意的是本項目用的是mybatis-plus做持久層,要給實體類指定自增ID策略

SysUser

package cn.zdxh.shirodemo.entity;

import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.enums.IdType;
import lombok.Data;

import java.util.Date;

/**
 * 用戶實體
 */
@Data
public class SysUser {
  //ID自動增長策略
  @TableId(value = "id", type = IdType.AUTO)
  private Integer id;
  private String username;
  private String password;
  private String salt;
  private Integer createUserId;
  private Date createTime;


}

7、持久層Mapper

 

  • 只給出SysUserMapper持久層相關,其他表的持久層同理
  • 繼承BaseMapper即可完成簡單的crud
  • 認證:需要查詢出用戶的信息
  • 授權:需要查詢角色和權限的信息

SysUserMapper

package cn.zdxh.shirodemo.mapper;

import cn.zdxh.shirodemo.entity.SysPerms;
import cn.zdxh.shirodemo.entity.SysRole;
import cn.zdxh.shirodemo.entity.SysUser;
import com.baomidou.mybatisplus.mapper.BaseMapper;

import java.util.List;

public interface SysUserMapper extends BaseMapper<SysUser> {
    /**授權相關*/
    //通過用戶ID查詢該用戶權限
    List<SysPerms> findAllPermsByUserId(Integer id);
    //通過用戶ID查詢該用戶角色
    List<SysRole> findAllRolesByUserId(Integer id);

    /**認證相關*/
    //通過用戶名查詢該用戶信息
    List<SysUser> findUserByUsername(String username);

}

在resources/mapper目錄下

SysUserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zdxh.shirodemo.mapper.SysUserMapper">
    <!--授權相關-->
    <!--通過用戶ID查詢該用戶權限-->
    <select id="findAllPermsByUserId" parameterType="int" resultType="cn.zdxh.shirodemo.entity.SysPerms">
        SELECT p.id,p.perms FROM sys_user_role ur
        LEFT JOIN sys_role r
        ON ur.role_id = r.id
        LEFT JOIN sys_role_perms rp
        ON r.id = rp.role_id
        LEFT JOIN sys_perms p
        ON rp.perms_id = p.id
        WHERE ur.user_id= #{id}
    </select>

    <!--通過用戶ID查詢該用戶角色-->
    <select id="findAllRolesByUserId" parameterType="int" resultType="cn.zdxh.shirodemo.entity.SysRole">
        SELECT r.id,r.create_user_id,r.role_name,r.role_remark,r.create_time
        FROM sys_user_role ur
        LEFT JOIN sys_role r
        ON ur.role_id = r.id
        WHERE ur.user_id = #{id}
    </select>

    <!--通過用戶名查詢該用戶信息-->
    <select id="findUserByUsername" parameterType="string" resultType="cn.zdxh.shirodemo.entity.SysUser">
        SELECT * FROM sys_user WHERE username = #{username}
    </select>

</mapper>

8、服務層

SysUserService

package cn.zdxh.shirodemo.service;

import cn.zdxh.shirodemo.entity.SysPerms;
import cn.zdxh.shirodemo.entity.SysRole;
import cn.zdxh.shirodemo.entity.SysUser;

import java.util.List;

public interface SysUserService {
    /**授權相關*/
    //通過用戶ID查詢該用戶權限
    List<SysPerms> findAllPermsByUserId(Integer id);
    //通過用戶ID查詢該用戶角色
    List<SysRole> findAllRolesByUserId(Integer id);

    /**認證相關*/
    //通過用戶ID查詢用戶
    List<SysUser> findUserByUsername(String username);

    /**操作相關*/
    //查詢所有用戶
    List<SysUser> findAllUsers();
    //新增用戶
    void addUser(SysUser sysUser);
}

SysUserServiceImpl

package cn.zdxh.shirodemo.service.impl;

import cn.zdxh.shirodemo.entity.SysPerms;
import cn.zdxh.shirodemo.entity.SysRole;
import cn.zdxh.shirodemo.entity.SysUser;
import cn.zdxh.shirodemo.mapper.SysUserMapper;
import cn.zdxh.shirodemo.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public List<SysPerms> findAllPermsByUserId(Integer id) {
        return sysUserMapper.findAllPermsByUserId(id);
    }

    @Override
    public List<SysRole> findAllRolesByUserId(Integer id) {
        return sysUserMapper.findAllRolesByUserId(id);
    }

    @Override
    public List<SysUser> findUserByUsername(String username) {
        return sysUserMapper.findUserByUsername(username);
    }

    @Override
    public List<SysUser> findAllUsers() {
        return sysUserMapper.selectList(null);
    }

    @Override
    public void addUser(SysUser sysUser) {
        sysUserMapper.insert(sysUser);
    }
}

 

9、自定義UserRealm,對接數據持久層(最重要的一步)

 

UserRealm

package cn.zdxh.shirodemo.shiro;

import cn.zdxh.shirodemo.entity.SysPerms;
import cn.zdxh.shirodemo.entity.SysRole;
import cn.zdxh.shirodemo.entity.SysUser;
import cn.zdxh.shirodemo.service.SysUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 自定義Realm
 */
@Component
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private SysUserService sysUserService;

    /**
     * 授權,需要授權纔會調用該方法
     * @param principalCollection
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //參數是從認證方法參數一傳過來的
        SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();
        //根據用戶ID去數據庫查詢角色和權限,並封裝角色名和權限名
        List<SysPerms> sysPerms = sysUserService.findAllPermsByUserId(sysUser.getId());
        List<SysRole> sysRoles = sysUserService.findAllRolesByUserId(sysUser.getId());
        Set<String> roles = new HashSet<>();
        Set<String> perms = new HashSet<>();
        //權限
        if (sysPerms != null && sysPerms.size() > 0){
            for (SysPerms  sysPerm : sysPerms){
                //分割出每個權限
                String[] split = sysPerm.getPerms().split(",");
                perms.addAll(Arrays.asList(split));
            }
        }
        //角色
        if (sysRoles != null && sysRoles.size() > 0){
            for (SysRole  sysRole : sysRoles){
                roles.add(sysRole.getRoleName());
            }
        }

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //設置擁有的角色
        authorizationInfo.setRoles(roles);
        //設置擁有的權限
        authorizationInfo.setStringPermissions(perms);
        return authorizationInfo;
    }

    /**
     * 登錄認證,即在subject.login(token)後調用
     * @param authenticationToken
     * @return
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //login方法調用之後傳過來的token
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)authenticationToken;
        //根據用戶名去數據庫查詢該用戶
        List<SysUser> sysUsers = sysUserService.findUserByUsername(usernamePasswordToken.getUsername());
        SysUser sysUser = new SysUser();
        sysUser = sysUsers.get(0);//只能一個用戶,其實這裏用戶名是唯一的

        /**
         * 參數一:傳給doGetAuthorizationInfo授權作爲參數的
         * 參數二:密碼,交給shiro自動判斷密碼是否正確
         * 參數三:鹽,給密碼加鹽後再判斷
         * 參數四:自定義Realm名稱
         */
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(sysUser, sysUser.getPassword(),
                ByteSource.Util.bytes(sysUser.getSalt()),getName());
        return authenticationInfo;
    }

    /**
     * 因爲了我們使用到了MD5算法,所以得告知憑證匹配器利用該算法去匹配。
     * 真正匹配的方法:assertCredentialsMatcher
     */
    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");//指定加密算法,默認只加密一次,可以選擇多次加密。這裏的加密要對應新建用戶時加密存儲的密碼
        super.setCredentialsMatcher(hashedCredentialsMatcher);
    }

//    public static void main(String[] args) {
//        try {
//            String password = new Md5Hash("123456").toString();
//            Md5Hash md5Hash = new Md5Hash("e10adc3949ba59abbe56e057f20f883e","f6281f61-ae3d-4b4c-8650-d73f4bf01208");
//            System.out.println(password);//e780cbb524f6f54e2734b3114978820f
//            System.out.println(md5Hash.toString());
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

   
    
}

doGetAuthorizationInfo:授權回調方法。去數據庫查詢該用戶的角色和權限
doGetAuthenticationInfo:認證回調方法。去數據庫查詢該用戶的相關信息
setCredentialsMatcher:需要加鹽加密才需要重寫該方法,否則不用

加密策略:

  • 密碼首先MD5加密一次
  • 隨機生成一個salt值,與加密密碼組合再MD5加密一次
  • 最後組成的加密密碼和鹽值存進數據庫

 

10、Controller層

 

LoginController:用於登錄和登出

  • 這裏要注意下新建用戶那裏,密碼的存儲策略
package cn.zdxh.shirodemo.controller;

import cn.zdxh.shirodemo.entity.SysUser;
import cn.zdxh.shirodemo.utils.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/login")
@Api(tags = "登錄相關接口")
public class LoginController {

    /**
     * 登錄接口
     * @param sysUser
     * @return
     */
    @PostMapping("/login")
    @ApiOperation("登錄接口")
    public Result<String> login(SysUser sysUser){
        //這裏先將密碼進行MD5,實際上這個步驟是前端做的
        sysUser.setPassword(new Md5Hash(sysUser.getPassword()).toString());
        //獲取到主體
        Subject subject = SecurityUtils.getSubject();
        //此對象目的是包裝用戶名和密碼
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(sysUser.getUsername(), sysUser.getPassword());
        //登錄
        subject.login(usernamePasswordToken);
        return Result.success("登錄成功");
    }

    /**
     * 退出登錄接口
     * @return
     */
    @GetMapping("/logout")
    @ApiOperation("退出登錄接口")
    public Result<String> logout(){
        //退出登錄操作
        SecurityUtils.getSubject().logout();
        return Result.error("退出登錄成功");
    }


    /**
     * 沒有登錄的回調接口
     * @return
     */
    @GetMapping("/loginfail")
    @ApiOperation("沒有登錄的回調接口")
    public Result<String> loginfail(){
        return Result.error("請登錄");
    }

    /**
     * 沒有權限的回調接口
     * @return
     */
    @GetMapping("/authfail")
    @ApiOperation("沒有權限的回調接口")
    public Result<String> authfail(){
        return Result.error("沒有操作權限");
    }

}

SysUserController:用於測試用戶權限

  • @RequiresPermissions() :說明該api需要相應權限才能訪問
  • @RequiresRoles() : 說明該api需要相應的角色才能訪問
  • 兩者存在一種即可,方法一的粒度更小,應用更加廣
package cn.zdxh.shirodemo.controller;

import cn.zdxh.shirodemo.entity.SysUser;
import cn.zdxh.shirodemo.service.SysUserService;
import cn.zdxh.shirodemo.utils.Result;
import cn.zdxh.shirodemo.utils.ShiroUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * 用戶接口
 */
@Api(tags = "用戶接口相關")
@RestController
@RequestMapping("/user")
public class SysUserController {

    @Autowired
    private SysUserService sysUserService;

    /**
     * 查詢所有的用戶
     * @return
     */
    @GetMapping("/list")
    @ApiOperation("查詢所有的用戶")
    @RequiresPermissions("sys:user:list")
    public Result<String> list(){
        //此方法和註解的方式一樣
        //SecurityUtils.getSubject().isPermitted("sys:user:list");
        List<SysUser> allUsers = sysUserService.findAllUsers();
        return Result.success(allUsers);
    }


    /**
     * 新增用戶
     * @return
     */
    @GetMapping("/add")
    @ApiOperation("新增用戶")
    @RequiresPermissions("sys:user:add")
    //@RequiresRoles("admin")
    public Result<String> add(SysUser sysUser){
        //此方法和註解的方式一樣
        //SecurityUtils.getSubject().isPermitted("sys:user:list");
        try{
            //密碼首先加一次密
            String password = ShiroUtils.encryptMD5(sysUser.getPassword());
            //加鹽之後再進行加密
            String salt = UUID.randomUUID().toString();
            password = ShiroUtils.encryptMD5(password,salt);

            //設置並初始化值
            sysUser.setPassword(password);
            sysUser.setSalt(salt);
            sysUser.setCreateTime(new Date());
            sysUserService.addUser(sysUser);
        }catch (Exception e){
            e.printStackTrace();
            return Result.error("新增用戶出現異常啦");
        }
        return Result.success("新增用戶成功");
    }

}

 

11、配置類 

 

 MyWebMvcConfigure : 配置swagger-ui 

package cn.zdxh.shirodemo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class MyWebMvcConfigure extends WebMvcConfigurationSupport {


    /**
    * 映射器,解決swagger-ui 404問題
    * @param registry
    */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/");
        // 解決 SWAGGER 404報錯
        registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

ShiroConfig:Shiro的相關配置 

package cn.zdxh.shirodemo.config;

import cn.zdxh.shirodemo.shiro.UserRealm;

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.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * 配置SecurityManager對象
     * @param userRealm 自定義的Realm對象
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setRememberMeManager(null);
        return securityManager;
    }

    /**
     * 配置過濾器,指定攔截哪些請求
     * @param securityManager
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        /**設置securityManager*/
        shiroFilter.setSecurityManager(securityManager);
        /**設置沒有登錄的回調方法*/
        shiroFilter.setLoginUrl("/login/loginfail");
        //設置沒有權限的回調方法,這裏有可能不會生效,因爲權限範圍問題,另一種解決方法爲統一異常管理器
        //shiroFilter.setUnauthorizedUrl("/login/authfail");

        /**設置過濾規則,anon:放行,authc:攔截*/
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/swagger/**", "anon");
        filterMap.put("/v2/api-docs", "anon");
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/swagger-resources/**", "anon");
        filterMap.put("/login/login","anon");
        filterMap.put("/login/logout","anon");
        filterMap.put("/login/fail","anon");

        filterMap.put("/**", "authc");
        shiroFilter.setFilterChainDefinitionMap(filterMap);

        return shiroFilter;
    }

    /**
     * 開啓Shiro的註解(如@RequiresRoles,@RequiresPermissions)
     * 配置以下兩個bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可實現此功能
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }



}

 

12、統一異常管理

 

MyExceptionHandler:因爲在沒權限的時候,在攔截器那裏跳轉頁面沒有生效,所以只能在這裏攔截沒權限異常

package cn.zdxh.shirodemo.exception;

import cn.zdxh.shirodemo.utils.Result;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 統一異常管理
 */
@ControllerAdvice
public class MyExceptionHandler {

    /**
     * 沒有權限的異常
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(AuthorizationException.class)
    public Result<String> authorizationException(AuthorizationException e){
        return Result.error("沒有操作權限");
    }

    /**
     * 其他的一些未知名異常
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public Result<String> exception(Exception e){
        return Result.error(e.getMessage());
    }
}


三、測試結果

 

訪問swagger-ui路徑:http://localhost:8080/swagger-ui.html#/

 

1、沒有登錄時訪問

 

2、登錄

 

3、登錄成功,並有相應權限

 

4、登錄成功,但是沒有相應權限

 

5、退出登錄

 

四、總結

 

  • Shiro權限管理系統是基於session實現的,這裏暫未滿足移動端的token認證方式,有興趣的可以在這方面探究一下。
  • 項目github地址:https://github.com/ningxiaojian31/shirodemo
  • 想要了解shiro的執行原理的可以翻看博主的另外一篇文章
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章