Springboot+Shiro+Mybatis实现权限管理

本文主要介绍Springboot+Shiro+Mybatis三者的整合

目录

1.项目结构

2.整合mybatis

2.1新建父工程

pom.xml

2.2 创建shiro-util模块

pom.xml

2.3 创建shiro-pojo模块

pom.xml

2.4 创建shiro-dao模块

UserMapper.java是接口

config.xml mybatis的配置文件

UserMapper.xml 查询数据库

2.5 创建shiro-service模块

pom.xml

UserService.java

UserServiceImpl.java

2.6 创建shiro-web模块

pom.xml

application.properties

UserController.java

3.整合Shiro框架

ShiroUserFilter.java

AuthenticationHandlerInterceptor.java

PermissionAccess.java

PermissionEnum.java

RoleEnum.java

ShiroRealm.java

ShiroConfig.java

WebMvcConfig.java

ShiroController.java

4.前端代码


 

1.项目结构

  • springboot-shiro-mybatis 是父目录
  • shiro-pojo 用来存放一些实体类
  • shiro-dao 持久化层,数据访问层,’连接数据库
  • shiro-service 服务层,写一些复杂的逻辑
  • shiro-web web端,一些web配置,访问的路径

数据库表

/*
SQLyog Ultimate v12.09 (64 bit)
MySQL - 5.7.21 : Database - wjx
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`wjx` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `wjx`;

/*Table structure for table `SysAuth` */

DROP TABLE IF EXISTS `SysAuth`;

CREATE TABLE `SysAuth` (
  `sysAuthId` int(20) NOT NULL AUTO_INCREMENT COMMENT '权限ID',
  `sysAuthCode` varchar(20) DEFAULT NULL COMMENT '权限编号',
  `sysAuthName` varchar(20) DEFAULT NULL COMMENT '权限名称',
  `sysAuthUrl` varchar(100) DEFAULT NULL COMMENT '请求的url',
  `sysAuthPermission` varchar(100) NOT NULL COMMENT '权限的名称',
  `sysAuthAva` int(1) DEFAULT '1' COMMENT '权限是否生效,0:未生效,1:生效',
  `sysAuthType` int(2) DEFAULT '2' COMMENT '权限类型,0:菜单,2:按钮',
  `sysAuthDes` varchar(100) DEFAULT NULL COMMENT '权限描述',
  PRIMARY KEY (`sysAuthId`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

/*Data for the table `SysAuth` */

insert  into `SysAuth`(`sysAuthId`,`sysAuthCode`,`sysAuthName`,`sysAuthUrl`,`sysAuthPermission`,`sysAuthAva`,`sysAuthType`,`sysAuthDes`) values (3001,'0001','查询用户','queryUser','queryUser',1,2,NULL),(3002,'0002','修改用户','updateUser','updateUser',1,2,NULL);

/*Table structure for table `SysRole` */

DROP TABLE IF EXISTS `SysRole`;

CREATE TABLE `SysRole` (
  `sysRoleId` int(2) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
  `sysRoleAva` int(2) NOT NULL DEFAULT '1' COMMENT '角色是否生效,0:未生效,1:生效',
  `sysRoleName` varchar(50) NOT NULL COMMENT '角色名称',
  `sysRoleDes` varchar(50) NOT NULL COMMENT '角色描述',
  PRIMARY KEY (`sysRoleId`)
) ENGINE=InnoDB AUTO_INCREMENT=2003 DEFAULT CHARSET=utf8;

/*Data for the table `SysRole` */

insert  into `SysRole`(`sysRoleId`,`sysRoleAva`,`sysRoleName`,`sysRoleDes`) values (2001,1,'admin','管理员'),(2002,1,'test','测试角色');

/*Table structure for table `SysRoleAuth` */

DROP TABLE IF EXISTS `SysRoleAuth`;

CREATE TABLE `SysRoleAuth` (
  `sysRoleId` int(20) DEFAULT NULL COMMENT '角色ID',
  `sysAuthId` int(20) DEFAULT NULL COMMENT '权限ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `SysRoleAuth` */

insert  into `SysRoleAuth`(`sysRoleId`,`sysAuthId`) values (2001,3001),(2001,3002),(2002,3001);

/*Table structure for table `SysUser` */

DROP TABLE IF EXISTS `SysUser`;

CREATE TABLE `SysUser` (
  `userId` int(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `userAccount` varchar(20) NOT NULL COMMENT '用户账户',
  `userPassword` varchar(20) NOT NULL COMMENT '用户密码',
  `userState` int(2) NOT NULL DEFAULT '2' COMMENT '用户状态,0:锁定,1:禁用,2:激活',
  PRIMARY KEY (`userId`)
) ENGINE=InnoDB AUTO_INCREMENT=1004 DEFAULT CHARSET=utf8;

/*Data for the table `SysUser` */

insert  into `SysUser`(`userId`,`userAccount`,`userPassword`,`userState`) values (1001,'wjx','123',2),(1002,'admin','123',2),(1003,'test','123',2);

/*Table structure for table `SysUserRole` */

DROP TABLE IF EXISTS `SysUserRole`;

CREATE TABLE `SysUserRole` (
  `sysUserId` int(20) DEFAULT NULL COMMENT '用户ID',
  `sysRoleId` int(20) DEFAULT NULL COMMENT '权限ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `SysUserRole` */

insert  into `SysUserRole`(`sysUserId`,`sysRoleId`) values (1001,2001),(1001,2002),(1002,2001),(1002,2002),(1003,2002);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

2.整合mybatis

2.1新建父工程

具体过程就不赘述了,父工程就是一个pom

配置了Springboot的版本,java版本,打包编译规则,还有一些modules依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wjx</groupId>
    <artifactId>springboot-shiro-mybatis</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>shiro-web</module>
        <module>shiro-pojo</module>
        <module>shiro-dao</module>
        <module>shiro-service</module>
        <module>shiro-util</module>
    </modules>

    <name>springboot-shiro-mybatis</name>
    <url>http://www.example.com</url>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <!--指定使用maven打包-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19.1</version>
                <configuration>
                    <skipTests>true</skipTests>  <!--默认关掉单元测试 -->
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.2 创建shiro-util模块

项目右键 New->Module,名称为shiro-util

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-shiro-mybatis</artifactId>
        <groupId>com.wjx</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shiro-util</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

</project>

Response.java文件

package com.wjx.util;

import lombok.Data;

/**
 * @Description:
 * @Auther: wjx
 * @Date: 2019/1/21 14:59
 */
@Data
public class Response {

    private final static String SUCCESS = "SUCCESS";
    private final static String FAIL = "FAIL";

    //返回代码
    private String code;
    //返回信息
    private String message;
    //返回数据
    private Object data;
    //是否成功
    private boolean isSuccess;

    /**
     * 成功
     *
     * @param data
     * @return
     */
    public static Response success(Object data) {
        Response response = new Response();
        response.code = SUCCESS;
        response.isSuccess = true;
        response.data = data;
        return response;
    }

    /**
     * 失败
     *
     * @param code
     * @param message
     * @return
     */
    public static Response fail(String code, String message) {
        Response response = new Response();
        response.code = code;
        response.message = message;
        response.isSuccess = false;
        return response;
    }

    /**
     * 失败
     *
     * @param message
     * @return
     */
    public static Response fail(String message) {
        Response response = new Response();
        response.code = FAIL;
        response.message = message;
        response.isSuccess = false;
        return response;
    }
}

2.3 创建shiro-pojo模块

同理创建shiro-pojo模块

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-shiro-mybatis</artifactId>
        <groupId>com.wjx</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shiro-pojo</artifactId>


    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.54</version>
        </dependency>

        <dependency>
            <groupId>com.wjx</groupId>
            <artifactId>shiro-util</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

实体类代码

@Data
public class SysAuth {
    private Integer sysAuthId;
    private String sysAuthCode; //权限编号
    private String sysAuthName; //权限名称
    private String sysAuthUrl; //权限请求的url 例如: user/login
    private String sysAuthPermission; //权限的的名称例如 user:login
    private Byte sysAuthAva; //权限是否有效
    private Byte sysAuthType; //权限类型。菜单还是按钮
    private String sysAuthDes; //权限描述
}

@Data
public class SysRole {
    private Integer sysRoleId;
    private Byte sysRoleAva; //角色是否生效
    private String sysRoleDes;//角色描述
    private String sysRoleName;//角色名称
}

@Data
public class SysUser {
    private Integer userId;
    private String userAccount;//用户账号
    private String userPassword;//用户密码
    private Integer userState;//用户状态
}

2.4 创建shiro-dao模块

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-shiro-mybatis</artifactId>
        <groupId>com.wjx</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shiro-dao</artifactId>


    <dependencies>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <dependency>
            <groupId>com.wjx</groupId>
            <artifactId>shiro-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

UserMapper.java是接口

package com.wjx.mapper;

import com.wjx.pojo.SysAuth;
import com.wjx.pojo.SysRole;
import com.wjx.pojo.SysUser;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;


/**
 * @Description:
 * @Auther: wjx
 * @Date: 2019/1/10 15:51
 */
@Mapper
public interface UserMapper {

    //查询用户集合
    List<SysUser> queryUser();

    //根据用户名称查询用户
    SysUser queryUserByUserName(SysUser sysUser);

    //根据用户名称查询角色
    List<SysRole> queryRoleByUserName(SysUser sysUser);

    //根据用户名称查询权限
    List<SysAuth> queryAuthByUserName(SysUser sysUser);
}

config.xml mybatis的配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!-- mybatis的配置文件 -->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.wjx.pojo"/>
    </typeAliases>
</configuration>

UserMapper.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="com.wjx.mapper.UserMapper">

    <select id="queryUserByUserName" resultType="SysUser">
       SELECT * FROM SysUser u WHERE u.`userAccount`= #{userAccount}
    </select>

</mapper>

 

2.5 创建shiro-service模块

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-shiro-mybatis</artifactId>
        <groupId>com.wjx</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shiro-service</artifactId>


    <dependencies>
        <dependency>
            <groupId>com.wjx</groupId>
            <artifactId>shiro-dao</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

UserService.java

package com.wjx.service;

import com.wjx.pojo.SysAuth;
import com.wjx.pojo.SysRole;
import com.wjx.pojo.SysUser;

import java.util.List;

/**
 * @Description:
 * @Auther: wjx
 * @Date: 2019/1/10 15:53
 */

public interface UserService {

    //查询用户集合
    List<SysUser> queryUser();

    //根据用户名称查询用户
    SysUser queryUserByUserName(SysUser sysUser);

    //根据用户名称查询角色
    List<SysRole> queryRoleByUserName(SysUser sysUser);

    //根据用户名称查询权限
    List<SysAuth> queryAuthByUserName(SysUser sysUser);
}

UserServiceImpl.java

package com.wjx.service.impl;

import com.wjx.mapper.UserMapper;
import com.wjx.pojo.SysAuth;
import com.wjx.pojo.SysRole;
import com.wjx.pojo.SysUser;
import com.wjx.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;


/**
 * @Description:
 * @Auther: wjx
 * @Date: 2019/1/10 15:55
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public List<SysUser> queryUser() {
        return userMapper.queryUser();
    }

    @Override
    public SysUser queryUserByUserName(SysUser sysUser) {
        return userMapper.queryUserByUserName(sysUser);
    }

    @Override
    public List<SysRole> queryRoleByUserName(SysUser sysUser) {
        return userMapper.queryRoleByUserName(sysUser);
    }

    @Override
    public List<SysAuth> queryAuthByUserName(SysUser sysUser) {
        return userMapper.queryAuthByUserName(sysUser);
    }
}

 

2.6 创建shiro-web模块

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-shiro-mybatis</artifactId>
        <groupId>com.wjx</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shiro-web</artifactId>


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

        <dependency>
            <groupId>com.wjx</groupId>
            <artifactId>shiro-service</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <!--打包-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!-- 指定该Main Class为全局的唯一入口 -->
                    <mainClass>com.Application</mainClass>
                    <!--<layout>ZIP</layout>-->
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

application.properties


#数据库地址
spring.datasource.url=jdbc:mysql://114.116.24.32:3306/wjx?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=wjx
spring.datasource.password=123

server.port=8082

#mybatis地址
mybatis.config-location=classpath:mybatis/config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml


#打印sql日志
logging.level.com.wjx.mapper=debug

UserController.java

package com.wjx.web.user;

import com.wjx.pojo.SysUser;
import com.wjx.service.UserService;
import com.wjx.util.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description:
 * @Auther: wjx
 * @Date: 2019/1/10 15:46
 */
@RestController
public class UserController {


    @Autowired
    private UserService userService;


    @GetMapping("queryUserByUserName")
    public Response queryUserByUserName(String userName) {
        Response response = null;
        SysUser sysUser = new SysUser();
        sysUser.setUserAccount(userName);
        try {
            SysUser user = userService.queryUserByUserName(sysUser);
            response = Response.success(user);
        } catch (Exception e) {
            e.printStackTrace();
            response = Response.fail(e.getMessage());
        }

        return response;
    }


}

运行Springboot,测试

 

3.整合Shiro框架

参考 https://blog.csdn.net/qq_35618489/article/details/86607932

添加Shiro的pom依赖,添加到 shiro-web 模块的pom里面

 <!--整合shiro权限框架--> 
<dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.4.0</version>
  </dependency>
  <!--shiro缓存插件-->
  <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>1.2.2</version>
</dependency>

更新 shiro-dao模块的UserMapper.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="com.wjx.mapper.UserMapper">

    <!--根据用户查询是否存在用户-->
    <select id="queryUserByUserName" resultType="SysUser">
       SELECT * FROM SysUser u WHERE u.`userAccount`= #{userAccount} and userPassword = #{userPassword}
    </select>

    <!--根据用户查询该用户的角色-->
    <select id="queryRoleByUserName" resultType="SysRole">
       SELECT
          r.*
        FROM
          SysUser u,
          SysUserRole ur,
          SysRole r
        WHERE u.`userId` = ur.`sysUserId`
          AND ur.`sysRoleId` = r.`sysRoleId`
          AND u.`userAccount` = #{userAccount}
    </select>

    <!--根据用户查询该用户对应的权限-->
    <select id="queryAuthByUserName" resultType="SysAuth">
       SELECT DISTINCT
          a.*
        FROM
          SysUser u,
          SysUserRole ur,
          SysRole r ,
          SysRoleAuth ra,
          SysAuth a
        WHERE u.`userId` = ur.`sysUserId`
          AND ur.`sysRoleId` = r.`sysRoleId`
          AND ra.`sysRoleId` = r.`sysRoleId`
          AND ra.`sysAuthId` = a.`sysAuthId`
          AND u.`userAccount` = #{userAccount}
    </select>

</mapper>

ShiroUserFilter.java:重写shiro的UserFilter,实现通过OPTIONS复杂请求

AuthenticationHandlerInterceptor.java: Shiro自定义拦截权限

PermissionAccess:自定义注解

PermissionEnum: 自定义权限

RoleEnum: 自定义角色

ShiroRealm:自定义Shiro的权限控制

ShiroConfig:Shiro的权限注入、拦截配置

WebMvcConfig:SpringMvc的Web配置类

ShiroController:测试类

主要代码

ShiroUserFilter.java

package com.wjx.config.shiro.filter;

import com.alibaba.fastjson.JSONObject;
import com.wjx.util.Response;
import org.apache.shiro.web.filter.authc.UserFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 重写shiro的UserFilter,实现通过OPTIONS请求
 *
 * @author wjx
 */
public class ShiroUserFilter extends UserFilter {

    /**
     * 在访问过来的时候检测是否为OPTIONS请求,如果是就直接返回true
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            setHeader(httpRequest, httpResponse);
            return true;
        }
        return super.preHandle(request, response);
    }

    /**
     * 该方法会在验证失败后调用,这里由于是前后端分离,后台不控制页面跳转
     * 因此重写改成传输JSON数据
     */
    @Override
    protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        saveRequest(request);
        setHeader((HttpServletRequest) request, (HttpServletResponse) response);
        PrintWriter out = response.getWriter();
        out.println(JSONObject.toJSONString(Response.fail("未登录")));
        out.flush();
        out.close();
    }

    /**
     * 为response设置header,实现跨域
     */
    public static void setHeader(HttpServletRequest request, HttpServletResponse response) {
        //跨域的header设置
        response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", request.getMethod());
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
        //防止乱码,适用于传输JSON数据
        response.setHeader("Content-Type", "application/json;charset=UTF-8");
        response.setStatus(HttpStatus.OK.value());
    }

}

AuthenticationHandlerInterceptor.java

package com.wjx.config.shiro.permiss;

import com.alibaba.fastjson.JSONObject;
import com.wjx.config.shiro.filter.ShiroUserFilter;
import com.wjx.util.Response;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description: Shiro自定义拦截权限
 * @Auther: wjx
 * @Date: 2019/1/21 17:59
 */
@Component
public class AuthenticationHandlerInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //设置编码格式
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json");

        //将handler转化为HandlerMethod,
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        //从方法处理器取出要调用的方法
        Method method = handlerMethod.getMethod();

        PermissionAccess annotation = method.getAnnotation(PermissionAccess.class);

        if (annotation == null) {
            return true;
        }

        //访问需要的权限
        PermissionEnum[] permissions = annotation.permission();
        //访问需要的角色
        RoleEnum[] roles = annotation.roles();

        try {
            Subject subject = SecurityUtils.getSubject();

            List<RoleEnum> roleFailList = new ArrayList<>();
            for (RoleEnum roleEnum : roles) {
                if (subject.hasRole(roleEnum.role)) { //shiro存在该角色
                    continue;
                }
                roleFailList.add(roleEnum);//不存在
            }

            //验证权限
            List<PermissionEnum> permissionFailList = new ArrayList<>();
            for (PermissionEnum permissionEnum : permissions) {
                if (subject.isPermitted(permissionEnum.url)) { //shiro框架存在权限
                    continue;
                }
                permissionFailList.add(permissionEnum);//不存在
            }
            StringBuilder builder = new StringBuilder();

            if (roleFailList.size() != 0) {
                roleFailList.stream().forEach(roleEnum -> {
                    builder.append(roleEnum.role + ",");
                });
                builder.append("角色权限不够");
                ShiroUserFilter.setHeader(request, response);
                response.getWriter().write(JSONObject.toJSONString(Response.fail(builder.toString())));
                return false;
            }

            if (permissionFailList.size() != 0) {
                permissionFailList.stream().forEach(permissionEnum -> {
                    builder.append(permissionEnum.url + ",");
                });
                builder.append("权限不足");
                ShiroUserFilter.setHeader(request, response);
                response.getWriter().write(JSONObject.toJSONString(Response.fail(builder.toString())));
                return false;
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
}

PermissionAccess.java

package com.wjx.config.shiro.permiss;

import java.lang.annotation.*;

/**
 * @Description: 自定义注解
 * @Auther: wjx
 * @Date: 2019/1/21 17:54
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface PermissionAccess {

    //权限
    PermissionEnum[] permission() default {};

    //角色
    RoleEnum[] roles() default {};

}

PermissionEnum.java

package com.wjx.config.shiro.permiss;

/**
 * @Description: 权限的枚举
 * @Auther: wjx
 * @Date: 2019/1/21 17:48
 */
public enum PermissionEnum {

    QUERY_USER("queryUser"),
    UPDATE_USER("updateUser");

    public String url;

    PermissionEnum(String url) {
        this.url = url;
    }

    public static PermissionEnum of(String urls) {
        for (PermissionEnum permissionEnum : PermissionEnum.values()) {
            if (permissionEnum.equals(urls)) {
                return permissionEnum;
            }
        }
        return null;
    }
}

RoleEnum.java

package com.wjx.config.shiro.permiss;

/**
 * @Description: 角色的枚举
 * @Auther: wjx
 * @Date: 2019/1/21 17:48
 */
public enum RoleEnum {

    ADMIN("admin"),

    TEST("test"),

    WB("wb");

    public String role;

    RoleEnum(String url) {
        this.role = url;
    }

    public static RoleEnum of(String role) {
        for (RoleEnum roleEnum : RoleEnum.values()) {
            if (roleEnum.equals(role)) {
                return roleEnum;
            }
        }
        return null;
    }
}

ShiroRealm.java

package com.wjx.config.shiro.realm;

import com.wjx.pojo.SysAuth;
import com.wjx.pojo.SysRole;
import com.wjx.pojo.SysUser;
import com.wjx.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @Description: 自定义Shiro的权限控制
 * @Auther: wjx
 * @Date: 2019/1/21 14:21
 * <p>
 * Shiro的错误类型
 * authc:
 * AuthencationException:
 *     AuthenticationException 异常是Shiro在登录认证过程中,认证失败需要抛出的异常。
 * <p>
 *     AuthenticationException包含以下子类:
 * <p>
 *     CredentitalsException 凭证异常
 * <p>
 *     IncorrectCredentialsException 不正确的凭证
 * <p>
 *     ExpiredCredentialsException 凭证过期
 * <p>
 *     AccountException 账号异常
 * <p>
 *     ConcurrentAccessException 并发访问异常(多个用户同时登录时抛出)
 * <p>
 *     UnknownAccountException 未知的账号
 * <p>
 *     ExcessiveAttemptsException 认证次数超过限制
 * <p>
 *     DisabledAccountException 禁用的账号
 * <p>
 *     LockedAccountException 账号被锁定
 * <p>
 *     UnsupportedTokenException 使用了不支持的Token
 * <p>
 *     
 * <p>
 * ###############################################################################################
 * <p>
 * <p>
 * authz:
 * AuthorizationException:
 * 子类:
 * <p>
 *     UnauthorizedException:抛出以指示请求的操作或对请求的资源的访问是不允许的。
 * <p>
 *     UnanthenticatedException:当尚未完成成功认证时,尝试执行授权操作时引发异常。
 *     (授权只能在成功的认证之后执行,因为授权数据(角色、权限等)必须总是与已知的标识相关联。这样的已知身份只能在成功登录时获得。)
 */
public class ShiroRealm extends AuthorizingRealm {

    /**
     * 正常这里需要连接数据库去读取配置,
     * 在写demo的时候这里先不这么做
     */

    @Autowired
    private UserService userService;


    /**
     * 配置权限,注入权限
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("----------------权限配置----------------");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        try {
            SysUser user = (SysUser) principalCollection.getPrimaryPrincipal();
            List<SysRole> sysRoleList = userService.queryRoleByUserName(user);
            /**
             * 注入角色
             */
            for (SysRole role : sysRoleList) {
                authorizationInfo.addRole(role.getSysRoleName());
            }

            List<SysAuth> sysAuthList = userService.queryAuthByUserName(user);
            /**
             * 注入权限
             */
            for (SysAuth auth : sysAuthList) {
                authorizationInfo.addStringPermission(auth.getSysAuthPermission());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return authorizationInfo;
    }

    /**
     * 用户验证
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //加这一步的目的是在Post请求的时候会先进认证,然后在到请求
        if (token.getPrincipal() == null) {
            return null;
        }
        //获取用户输入的账户
        String userName = (String) token.getPrincipal();
        //这里需注意。看别人的教程有人是这样写的String password = (String) token.getCredentials();
        //项目运行的时候报错,发现密码不正确。后来进源码查看发现将密码注入后。Shiro会进行转义将字符串转换成字符数组。
        //源码:this(username, password != null ? password.toCharArray() : null, false, null);
        //不晓得是否是因为版本的原因,建议使用的时候下载源码进行查看
        String password = new String((char[]) token.getCredentials());

        SysUser sysUser = userService.queryUserByUserName(new SysUser(userName, password));

        if (!sysUser.getUserAccount().equals(userName)) {
            throw new UnknownAccountException(); //账户不存在
        } else {
            if (!password.equals(sysUser.getUserPassword())) {
                throw new IncorrectCredentialsException();
            }
            if (0 == sysUser.getUserState()) {
                throw new LockedAccountException();//账户被锁定
            } else if (1 == sysUser.getUserState()) {
                throw new DisabledAccountException();//账户被禁用
            } else {
                SimpleAuthenticationInfo authorizationInfo = new SimpleAuthenticationInfo(sysUser, sysUser.getUserPassword(), getName());
                return authorizationInfo;
            }
        }

    }
}

ShiroConfig.java

package com.wjx.config.shiro;

import com.wjx.config.shiro.filter.ShiroUserFilter;
import com.wjx.config.shiro.realm.ShiroRealm;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Description: Shiro的权限注入、拦截配置
 * @Auther: wjx
 * @Date: 2019/1/21 15:14
 */

@Configuration
public class ShiroConfig {
    /**
     * 将自己的验证方法加入到容器
     *
     * @return
     */
    @Bean
    public ShiroRealm shiroRealm() {
        return new ShiroRealm();
    }

    /**
     * 权限管理,配置主要是Realm的管理认证
     *
     * @return
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm());
        return securityManager;
    }

    /**
     * Filter工厂,配置过滤条件和跳转条件
     *
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        System.out.println("--------------------shiro filter-------------------");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //添加过滤器
        Map<String, Filter> filterMap = new HashMap<>();
        ShiroUserFilter myPassFilter = new ShiroUserFilter();
        filterMap.put("authc", myPassFilter);
        shiroFilterFactoryBean.setFilters(filterMap);

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //注意过滤器配置顺序 不能颠倒
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了,登出后跳转配置的loginUrl
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/favicon.ico", "anon");
        //拦截其他所以接口
        filterChainDefinitionMap.put("/**", "authc");
        //配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接 自行处理。不用shiro进行跳转
        // shiroFilterFactoryBean.setSuccessUrl("user/index");
        //未授权界面;
        //shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;


    }
    /**
     * 加入注解的使用,不加入这个验证权限注解不生效
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

 

WebMvcConfig.java

package com.wjx.config;

import com.wjx.config.shiro.permiss.AuthenticationHandlerInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * @Description: SpringMvc的Web配置类
 * @Auther: wjx
 * @Date: 2019/1/21 16:39
 */
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {


    private String[] allowedOrigins = {"http://localhost:9090", "http://localhost:9091", "http://localhost:9092"};

    /**
     * 解决前后端跨域
     *
     * @param registry
     */
    @Override
    protected void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/**") //对所有的路径都进行拦截
                .allowedOrigins(allowedOrigins) //绑定IP,允许来自这个IP地址的cookie和session携带过来
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") //允许的方法
                .allowCredentials(true)  //允许携带cookie
                .maxAge(3600); //设置最大缓存
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthenticationHandlerInterceptor());
    }
}

ShiroController.java

package com.wjx.web.shiro;

import com.wjx.config.shiro.permiss.PermissionAccess;
import com.wjx.config.shiro.permiss.PermissionEnum;
import com.wjx.config.shiro.permiss.RoleEnum;
import com.wjx.pojo.SysUser;
import com.wjx.util.Response;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

/**
 * @Description:
 * @Auther: wjx
 * @Date: 2019/1/21 14:57
 */
@RestController
public class ShiroController {


    @RequestMapping("/login")
    public Object Login(HttpServletRequest request) {

        System.out.println("*****************************************************");
        SysUser user = new SysUser();
        user.setUserAccount("wjx");
        user.setUserPassword("123");

        Response response = null;
        Subject subject = SecurityUtils.getSubject();
        //数据库的密码我进行了Md5加密。如果没有进行加密的无需这个
        //user.setUserPassword();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUserAccount(), user.getUserPassword());
        try {
            subject.login(token);
            response = Response.success("登录成功");
        } catch (UnknownAccountException e) {
            response = Response.fail("用户名不存在");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            response = Response.fail("密码错误");
        } catch (LockedAccountException e) {
            response = Response.fail("用户没有被激活");
        } catch (DisabledAccountException e) {
            response = Response.fail("用户被禁用");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return response;
    }


    /**
     * GRT 请求
     *
     * @return
     */
    @RequestMapping("queryUser")
    @PermissionAccess(permission = PermissionEnum.UPDATE_USER, roles = RoleEnum.WB)
    public Object queryUser() {
        System.out.println("queryUser");
        return Response.success("queryUser");
    }

    /**
     * 复杂请求POST测试
     *
     * @return
     */

    @RequestMapping(value = "/getPost", method = RequestMethod.POST)
    public Response index(@RequestBody SysUser user) {
        return Response.success(user);
    }

}

测试截图

 

4.前端代码

function login() {
		$.ajax({
			url : "http://localhost:8082/login",
			type : 'get',
			dataType : "json",
			xhrFields : {
				withCredentials : true
			},
			crossDomain : true,
			success : function(data) {
				console.log(data);
			},
			error : function(error, textStatus, errorThrown) {
				console.log("error", error);
				console.log("状态码:", error.status);
				console.log("textStatus:", textStatus);
				console.log("errorThrown:", errorThrown);
			}
		});
	}

	function queryUser() {
		$.ajax({
			url : "http://localhost:8082/queryUser",
			type : 'get',
			dataType : "json",
			xhrFields : {
				withCredentials : true
			},
			crossDomain : true,
			success : function(data) {
				console.log(data);
			},
			error : function(error, textStatus, errorThrown) {
				console.log("error", error);
				console.log("状态码:", error.status);
				console.log("textStatus:", textStatus);
				console.log("errorThrown:", errorThrown);
			}
		});
	}

	function postTest() {
		var user = {
			"userAccount" : "wjx",
			"userPassword" : "123"
		};
		var data = JSON.stringify(user);

		$.ajax({
			async : true,
			type : "post",
			url : "http://localhost:8082/getPost",
			data : data,
			dataType : "json",
			contentType : "application/json; charset=utf-8",
			crossDomain : true,
			xhrFields : {
				withCredentials : true
			},

			dataType : "json",
			success : function(data) {
				console.log(data);
			},
			error : function(data) {
				console.log(data)
			}
		})
	}

 

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值