springboot整合shiro和jwt-完整版

前言

shiro框架是如今web开发做权限控制的重要选择之一,除了这个之外选择spring security的也很多。两者都有各自的优点。shiro更加简单,而spring security本来就是spring家族的,api整合起来更加友好。博主在实际项目中只用过shiro,相比spring security,shiro简单一些。但是这些安全框架都不是特别容易理解。
在项目中权限控制的思路是:首先用户可以凭借手机号+密码登录,也可以凭借手机号+验证码登录。如果登陆成功将颁发给用户一个token,token由jwt制作。往后token过期之前,用户访问的凭借都是token。如果token过期或者被篡改,则要求用户重新登陆。在token有效时,用户访问需要权限的接口都要经过shiro的两个核心注解RequiresRoles以及RequiresPermissions的验证。
下面将一点点带大家一步步看项目如何实现权限控制的。

如果对security有兴趣的同学,可以去看看我朋友写的系列文章:spring-security

项目整体结构说明

在这里插入图片描述

本来是准备采取完全的ddd方式来构建项目,但是考虑到理解shiro可能已经有些费力。就不写得那么ddd了(你现在大概在心里喷博主是不会ddd的,hhh)。项目的四层的含义稍微解释一下

interfaces:用户接口层,这里面的facade里面放着大家熟悉的controller,其余都是用来转换
前后端的对象的,例如DTO。

infrastructure:基础设施层,这里面存放的是一些配置信息、工具类以及shiro相关的一些类,
你可以理解为此层是为了给整个项目提供通用支持。

appication:应用服务层,进入微服务的入口。此层组织多个业务领域完成来自facade的业务
需求,除此以外,一些spring的事件、定时任务、消息中间件的东西都会放在这里。

domain层:领域模型层,ddd领域驱动模型最重要但是最难理解的一层。这里面会存在多个聚合,
每一个聚合都是独立的,这种独立体现在业务上。例如用户周围的一些概念,用户+角色+权限可
以形成一个聚合,系统日志和用户操作日志可以形成一个聚合。这一层为了代码的简易没有按照
ddd严格划分领域,感兴趣的伙伴可以去看一些书籍了解。

项目使用的数据库表

本来不打算将sql语句写在这里,但是如果不想去gitee看源码的小伙伴就拿着这里的sql用吧。
第一张 用户表

在这里插入图片描述

1
2
3
4
5
6
7
8
9
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'user表的id字段',
`userId` varchar(255) NOT NULL COMMENT '用户id 作为表主键 用于关联',
`userName` varchar(25) DEFAULT NULL COMMENT '用户登录帐号',
`password` varchar(255) DEFAULT NULL COMMENT '用户登录密码',
`userRemarks` varchar(255) DEFAULT NULL COMMENT '备注,预留字段',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC

第二张 角色表

在这里插入图片描述

1
2
3
4
5
6
7
CREATE TABLE `sys_role` (
`roleId` varchar(32) NOT NULL COMMENT ' 角色id 作为表主键 用于关联',
`roleName` varchar(32) DEFAULT NULL COMMENT '角色名',
`roleRemarks` varchar(255) DEFAULT NULL COMMENT '备注,预留字段',
PRIMARY KEY (`roleId`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC

第三张 用户角色中间表

在这里插入图片描述

1
2
3
4
5
6
7
8
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '表主键id',
`userId` varchar(255) DEFAULT NULL COMMENT '帐号表的主键id',
`roleId` varchar(32) DEFAULT NULL COMMENT '角色表的主键id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC


第四张 权限表

在这里插入图片描述

1
2
3
4
5
6
7
CREATE TABLE `sys_permissions` (
`perId` varchar(32) NOT NULL COMMENT '权限表id 作为表主键 用于关联',
`permissionsName` varchar(32) DEFAULT NULL COMMENT '权限名称',
`perRemarks` varchar(255) DEFAULT NULL COMMENT '备注,预留字段',
PRIMARY KEY (`perId`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC

第五张 角色权限中间表

在这里插入图片描述

1
2
3
4
5
6
7
8
CREATE TABLE `role_per` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '表主键id',
`roleId` varchar(32) DEFAULT NULL COMMENT '角色表的主键id',
`perId` varchar(32) DEFAULT NULL COMMENT '权限表的主键id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC


以上就是表的结构和所有创建表的语句,你只需要建立一个mysql的数据库将这些语句都执行一下即可。如果你连数据都不想插入,最后我会给你在gitee的项目地址,项目里面有sql脚本,直接拿去用就好。五张表中sys_user代表的就是用户的账号,主要的字段就是手机号和密码字段。然后每个用户都会有一个角色,用户一旦创建,就会往user_role中添加关于这个用户的相关角色。然后角色和权限的关系是一对多的,由此实现用户的权限控制。项

目所用依赖、配置文件、启动类

springboot选择了较高版本的2.2.6。shiro选择1.3.2。Jwt选择3.4.0。属于springboot的依赖遵循夫版本,剩余依赖各自的版本即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<?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.cmdc</groupId>
<artifactId>shiro-common</artifactId>
<version>1.0-SNAPSHOT</version>

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

<dependencies>
<!--web项目-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--spring redis starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--mysql连接驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--lombok简化pojo代码-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<!--shiro权限框架-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<!--JWT-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

配置文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server:
port: 8888
spring:
application:
name: my-shiro
#指定数据库相关信息
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/shiro-end-starter?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
username: root
password: root
mybatis:
mapper-locations: classpath*:com.cmdc.domain.mapper/*.xml
type-aliases-package: com.cmdc.domain.entity
configuration:
map-underscore-to-camel-case: true

启动类——最重要的MapperScan注解不要忘记

```java
package com.cmdc;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;