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/> </parent>
<dependencies> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency> <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> <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;