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的执行原理的可以翻看博主的另外一篇文章
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章