1.前期设计

1.1 功能模块的设计

用户权限管理系统一般包括以下模块:

1.2 数据库的设计

根据基本的功能可以总结出6张数据库表:

  • sys_permission:权限表
  • sys_role:角色表
  • sys_role_permission:角色-权限关联表
  • sys_user:用户表
  • sys_user_role:用户角色关联表
  • notify:通知表

接下来就是创建数据库acs和表的具体设计了。

1.sys_permission:
CREATE TABLE `sys_permission`
(`id`              int(4) unsigned NOT NULL AUTO_INCREMENT,`insert_time`     timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP,`update_time`     timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`permission_code` varchar(255)    NOT NULL COMMENT '权限的代码/通配符',`permission_name` varchar(255)    NOT NULL COMMENT '权限的中文释义',PRIMARY KEY (`id`),UNIQUE KEY `uk_permission_code` (`permission_code`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4COLLATE = utf8mb4_general_ci COMMENT ='权限表';

权限表用于将系统中所有支持进行权限控制的资源及其相关操作进行声明。

WildcardPermission 是 Shiro Permission 接口的一个实现,它允许我们将权限表示为通配符的方式,通配符字符串能作为方法参数传递给 Subject.isPermitted 以完成权限检查。例如:

  • “notify:edit”:表示 “编辑通知” 权限。
  • “notify:*”:表示对通知的任何操作。

接下来需要将系统中所有的权限都定义好,然后插入到数据库中。

INSERT INTO `sys_permission`(id, permission_code, permission_name)
VALUES (101, 'notify:list', '通知列表'),(102, 'notify:add', '创建通知'),(103, 'notify:edit', '编辑通知'),(104, 'notify:delete', '删除通知'),(301, 'user:list', '用户列表'),(302, 'user:add', '新增用户'),(303, 'user:edit', '编辑用户'),(304, 'user:delete', '删除用户'),(501, 'role:list', '角色列表'),(502, 'role:add', '新增角色'),(503, 'role:edit', '编辑角色'),(504, 'role:delete', '删除角色');
2.sys_role:
CREATE TABLE `sys_role`
(`id`          int(4) unsigned NOT NULL AUTO_INCREMENT,`insert_time` timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP,`update_time` timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`role_name`   varchar(20)     NOT NULL COMMENT '角色名',PRIMARY KEY (`id`),UNIQUE KEY `uk_role_name` (`role_name`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4COLLATE = utf8mb4_general_ci COMMENT ='角色表';

我们对具有不同权限的系统用户进行分类,将其分为不同的角色,并保存到角色表中,如 ”通知管理员“,”权限管理员“等。这样我们只需要为用户关联其对应的角色,就可以完成用户的权限分配。

接下来为角色表创建超级管理员这个角色。

INSERT INTO `sys_role`(id, role_name)
VALUES (1, '超级管理员');
3.sys_user:
CREATE TABLE `sys_user`
(`id`          int(4) unsigned NOT NULL AUTO_INCREMENT,`insert_time` timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP,`update_time` timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`username`    varchar(255)    NOT NULL COMMENT '用户名',`password`    varchar(255)    NOT NULL COMMENT '密码',`nickname`    varchar(255)    NULL     DEFAULT NULL COMMENT '昵称',PRIMARY KEY (`id`),UNIQUE KEY `username` (`username`)
) ENGINE = InnoDBAUTO_INCREMENT = 10000DEFAULT CHARSET = utf8mb4COLLATE = utf8mb4_general_ci COMMENT ='用户表';

用户表记录了用户的身份信息,这里记录着登录时对登录者进行身份认证所需标准凭证(用户名,密码)。

接下来为用户表创建超级管理员用户。

INSERT INTO `sys_user`(id, username, password, nickname)
VALUES (10001, 'admin', '123456', '超级管理员');
4.sys_role_permission:
CREATE TABLE `sys_role_permission`
(`id`            int(4) unsigned NOT NULL AUTO_INCREMENT,`insert_time`   timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP,`update_time`   timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`role_id`       int(4)          NOT NULL COMMENT '角色id',`permission_id` int(4)          NOT NULL COMMENT '权限id',PRIMARY KEY (`id`),UNIQUE KEY `uk_role_id_permission_id` (`role_id`, `permission_id`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4COLLATE = utf8mb4_general_ci COMMENT ='角色-权限关联表';

一个角色可以拥有多个权限,这些关联关系将由单独的关联表负责:角色-权限关联表。

接下来角色-权限关联表创建后为超级管理员分配所有权限:

INSERT INTO `sys_role_permission`(role_id, permission_id)
VALUES (1, 101),(1, 102),(1, 103),(1, 104),(1, 301),(1, 302),(1, 303),(1, 304),(1, 501),(1, 502),(1, 503),(1, 504);
5.sys_user_role :
CREATE TABLE `sys_user_role`
(`id`          int(4) unsigned NOT NULL AUTO_INCREMENT,`insert_time` timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP,`update_time` timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`user_id`     int(4)          NOT NULL COMMENT '用户id',`role_id`     int(4)          NOT NULL COMMENT '角色id',PRIMARY KEY (`id`),UNIQUE KEY `uk_user_role` (`user_id`, `role_id`)
) ENGINE = InnoDBAUTO_INCREMENT = 10000DEFAULT CHARSET = utf8mb4COLLATE = utf8mb4_general_ci COMMENT ='用户-角色关联表';

一个用户可以拥有多个角色,这些关联关系将由单独的关联表负责:用户-角色关联表。

接下来创建用户-角色关联表后为将超级管理员的角色与用户关联:

INSERT INTO `sys_user_role`(user_id, role_id)
VALUES (10001, 1);
6.notify:
CREATE TABLE `notify`
(`id`          int(4) unsigned NOT NULL AUTO_INCREMENT,`insert_time` timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP,`update_time` timestamp       NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`user_id`     int(4)          NOT NULL COMMENT '创建用户id',`content`     text            NOT NULL COMMENT '通知内容',PRIMARY KEY (`id`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4COLLATE = utf8mb4_general_ci COMMENT ='通知表';

2.初始化项目

继续使用上一篇文章中创建的项目,添加必要的初始依赖。

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.modelmapper</groupId><artifactId>modelmapper</artifactId><version>2.1.1</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.5.3</version></dependency>

2.1 创建分页工具类

对于需要进行分页的数据集,我们需要为其定义一个统一的数据结构,这里将其定义到 PageModel.java 中。创建common包,在该包下创建PageModel.java类。

@Data
public class PageModel<T> implements Serializable {private int pageNum;private int pageSize;private long total;private int pages;private List<T> list;
}

2.2 统一异常处理

1.导致请求处理失败的错误:由于应用存在未解决的 bug 或依赖的资源报错而引起的错误,如试图打开不存在文件导致的文件 io 错误;网络请求超时,网络 io 错误;或者是强依赖的三方资源报错;甚至是操作系统 OOM,SOF 等。如果遇到这些错误,用户的请求往往无法继续处理,我们能做的只有给用户提示请求处理失败。

2.非法用户输入引起的错误:用户(这里的用户不仅仅指实际用户,也指 API 使用者)输入是不可预测的,难免输入系统无法处理的内容,我们应该尽可能多的主动监测这类错误,监测到时先中断请求,并提示用户需要进行修正,然后才能再次发起请求。

3.进行处理后仍有可能使请求成功完成的错误:这类错误在 java 中我们可以通过 try - catch - finally 进行处理。

在common包下创建两类异常,用于作为上述异常的代表。
ServiceException.java

public class ServiceException extends RuntimeException {public ServiceException() {}public ServiceException(String message) {super(message);}public ServiceException(String message, Throwable cause) {super(message, cause);}public ServiceException(Throwable cause) {super(cause);}public ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);}
}

UserInputException

public class UserInputException extends RuntimeException {public UserInputException(String message) {super(message);}
}

接着提供统一的返回结果类,出现异常错误,同样需要使用统一的返回结果返回数据,这里使用使用Spring Boot自带的返回响应包装类。在common包下创建Results.java类。
Results

public class Results {public static ResponseEntity success() {ResponseEntity rm = new ResponseEntity(HttpStatus.OK);return rm;}public static ResponseEntity userInputError(String msg) {ResponseEntity rm = new ResponseEntity(msg, HttpStatus.BAD_REQUEST);return rm;}public static ResponseEntity error(String msg) {ResponseEntity rm = new ResponseEntity(msg, HttpStatus.INTERNAL_SERVER_ERROR);return rm;}public static <T extends Serializable> ResponseEntity<T> success(T data) {ResponseEntity<T> rm = new ResponseEntity<T>(data, HttpStatus.OK);return rm;}
}

然后在 Spring 提供的 ControllerAdvice 中统一处理,创建api包,在该包下创建ExceptionController.java类,使用 ControllerAdvice 统一处理错误。
ExceptionController

@Slf4j
@ControllerAdvice
public class ExceptionController {/*** 出现UserInputException异常时,该方法会处理请求返回响应* @param e* @return*/@ExceptionHandler({UserInputException.class})@ResponseBodypublic ResponseEntity userInputException(UserInputException e) {log.warn(e.getMessage());return Results.userInputError(e.getMessage());}/*** 出现ServiceException异常时,该方法会处理请求返回响应* @param e* @return*/@ExceptionHandler({ServiceException.class})@ResponseBodypublic ResponseEntity serviceException(ServiceException e) {log.error("未知错误", e);return Results.error("未知错误");}
}

2.3 集成Swagger2接口文档

swagger 可以帮助我们自动生成 API 文档,节省了维护文档的时间,并且可一完成接口的基本测试。

2.3.1 添加依赖
        <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.6.1</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.6.1</version></dependency>
2.3.2 启用 Swagger2

在 Application.java 中添加 @EnableSwagger2 注解启用 Swagger2。

@SpringBootApplication
@EnableSwagger2
public class SpringbootshiroApplication {public static void main(String[] args) {SpringApplication.run(SpringbootshiroApplication.class, args);}
}
2.3.3 进行简要测试

在包api下,创建testController.java类,在该类中使用Swagger2接口文档。

@RestController
public class testController {/*** 简要测试Swagger2* @return*/@GetMapping("/hello")@ApiOperation("简单的测试")public String test(){return "hello world";}
}

启动后打开 http://localhost:8080/swagger-ui.html 页面。此时出现项目启动报错,我们看看具体的错误:

org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException

出现以上错误,是由Springfox的一个bug引起的。具体来说,它假设MVC的路径匹配将使用基于Ant-based的路径匹配器,而不是基于PathPattern-based的匹配器。基于PathPattern-based的匹配已经是一个选项,并且是SpringBoot2.6的默认选项。

因此,如Spring Boot 2.6发行说明中所述,您可以通过在application.properties文件中将Spring.mvc.pathmatch.matching-strategy设置为ant path matcher来恢复Springfox假定将使用的配置。请注意,只有在不使用Spring Boot的执行器时,此功能才起作用。无论配置的匹配策略如何,执行器始终使用基于路径模式的解析。如果您想在Spring Boot 2.6及更高版本中将其与执行器一起使用,则需要对Springfox进行更改。

所以我们在主配置中添加一个配置:

# 解决集成Swagger2后启动报错
spring.mvc.pathmatch.matching-strategy = ant_path_matcher

继续启动后打开 http://localhost:8080/swagger-ui.html 页面。

可以看到Swagger2接口文档被集成到Spring Boot项目中。

3.使用mybatis逆向工程

这里持久层使用mybatis框架,该框架可以使用简单的配置自动生成必要的Entity,Dao,Mapper文件。

3.1 添加依赖及插件

        <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.1.10</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.1</version></dependency><!-- mybatis-generator 自动生成代码插件 --><plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.3.2</version><configuration><configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile><overwrite>true</overwrite><verbose>true</verbose></configuration></plugin>

3.2 添加 generatorConfig.xml 配置文件

在resources/generator 目录下创建 generatorConfig.xml 配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"><generatorConfiguration><!-- 数据库驱动:选择你的本地数据库驱动包 --><classPathEntry  location="/Users/xuzhi/devTools/local-maven/mysql/mysql-connector-java/8.0.22/mysql-connector-java-8.0.22.jar"/><context id="DB2Tables"  targetRuntime="MyBatis3"><commentGenerator><property name="suppressDate" value="true"/><!-- 是否去除自动生成的注释 true:是; false:否 --><property name="suppressAllComments" value="true"/></commentGenerator><!-- 数据库链接URL,用户名、密码 --><jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1:3306/acs" userId="root" password="12345678"></jdbcConnection><javaTypeResolver><property name="forceBigDecimals" value="false"/></javaTypeResolver><!-- 生成模型的包名和位置 --><javaModelGenerator targetPackage="com.picacho.springbootshiro.entity" targetProject="src/main/java"><property name="enableSubPackages" value="true"/><property name="trimStrings" value="true"/></javaModelGenerator><!-- 生成映射文件的包名和位置 --><sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"><property name="enableSubPackages" value="true"/></sqlMapGenerator><!-- 生成DAO的包名和位置 --><javaClientGenerator type="XMLMAPPER" targetPackage="com.picacho.springbootshiro.dao" targetProject="src/main/java"><property name="enableSubPackages" value="true"/></javaClientGenerator><!-- 要生成的表 tableName 是数据库中的表名或视图名 domainObjectName 是实体类名 --><table tableName="sys_permission" domainObjectName="SysPermission" enableCountByExample="false"enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"selectByExampleQueryId="false"></table><table tableName="sys_role" domainObjectName="SysRole" enableCountByExample="false"enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"selectByExampleQueryId="false"/><table tableName="sys_role_permission" domainObjectName="SysRolePermission" enableCountByExample="false"enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"selectByExampleQueryId="false"/><table tableName="sys_user" domainObjectName="SysUser" enableCountByExample="false"enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"selectByExampleQueryId="false"/><table tableName="sys_user_role" domainObjectName="SysUserRole" enableCountByExample="false"enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"selectByExampleQueryId="false"/><table tableName="notify" domainObjectName="Notify" enableCountByExample="false"enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"selectByExampleQueryId="false"/></context>
</generatorConfiguration>

按照如下方式启动插件,生成我们需要的代码,如下图为生成后的文件。

注意生成的Dao接口还没有被注入到Spring容器中,所以需要我们手动添加注解,提示Sprin扫描这些Dao注入到容器中。

  • 第一种是在每个Dao接口上添加@Mapper注解。
  • 第二种在启动类上添加@MapperScan注解,并且指定扫描路径,这里是"com.picacho.springbootshiro.dao"。

这里我们选择第二种较为方便些。

3.3 添加mybatis配置

在resources文件夹下添加mybatis-config.xml文件,配置生成Entity,Dao,Mapper文件的基本配置。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><setting name="useGeneratedKeys" value="true"/><setting name="mapUnderscoreToCamelCase" value="true"/><setting name="logImpl" value="SLF4J"/><setting name="cacheEnabled" value="true"/><setting name="localCacheScope" value="SESSION"/></settings><plugins><plugin interceptor="com.github.pagehelper.PageInterceptor"><!-- 4.0.0以后版本可以不设置该参数 --><!--<property name="dialect" value="mysql"/>--><!-- 该参数默认为false --><!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 --><!-- 和startPage中的pageNum效果一样--><property name="offsetAsPageNum" value="true"/><!-- 该参数默认为false --><!-- 设置为true时,使用RowBounds分页会进行count查询 --><property name="rowBoundsWithCount" value="true"/><!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 --><!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)--><property name="pageSizeZero" value="true"/><!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 --><!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 --><!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 --><property name="reasonable" value="false"/><!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 --><!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 --><!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,orderBy,不配置映射的用默认值 --><!-- 不理解该含义的前提下,不要随便复制该配置 --><property name="params" value="pageNum=pageHelperStart;pageSize=pageHelperRows;"/><!-- 支持通过Mapper接口参数来传递分页参数 --><property name="supportMethodsArguments" value="false"/><!-- always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page --><property name="returnPageInfo" value="none"/></plugin></plugins>
</configuration>
  • useGeneratedKeys:允许 JDBC 支持自动生成主键,这样 Mapper(Mapper 主要有两种形式:XML 和 java 注解)中的 insert 操作会将自增的 id 回填到 Entity 中。useGeneratedKeys 是一个全局配置,但只对接口映射器(注解的方式)有效,对 XML 无效,因此在 XML 中我们还是需要为每个 insert 语句进行下面的指定:
<insert id="insert" useGeneratedKeys="true" keyProperty="id"></insert>
  • mapUnderscoreToCamelCase:将下划线风格的数据库列名自动映射为 java 字段的驼峰风格。
  • logImpl:将 SQL 执行语句通过日志打印出来。
  • localCacheScope:控制一级缓存生效范围,有 SESSION 和 STATEMENT 两种。MyBatis 会将 Mapper 中的查询语句()映射为 MappedStatement,执行查询时 MappedStatement 及其查询参数和返回结果会被缓存,下次同样的查询被执行时可以直接从缓存中获取结果。
  • cacheEnabled:控制二级缓存,一级缓存的最大生效范围是 SESSION,如果需要在多个 SESSION(即跨 SqlSession 实例)中进行缓存共享,就需要启用二级缓存。

3.4 在主配置文件种添加数据库相关配置

spring.datasource.url = jdbc:mysql://localhost:3306/acs?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = 12345678
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name = com.mysql.jdbc.Driver

4.Shiro配置

4.1 引入依赖

在Spring Boot项目中,Shiro 官方提供了专门的 Starter:shiro-spring-boot-web-starter依赖来简化集成。

        <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-web-starter</artifactId><version>1.4.0</version></dependency>

4.2 配置Shiro

在主配置文件中对Shiro进行配置。

# Shiro配置
shiro.web.enabled = true
shiro.sessionManager.sessionIdCookieEnabled = true
shiro.sessionManager.sessionIdUrlRewritingEnabled = true
shiro.loginUrl = /login.html
  • web.enabled 控制是否启用 Shiro 框架,true 表示启用,在项目开发阶段为了跳过安全检查可以设置为 false。
  • sessionManager.sessionIdCookieEnabled:启用或停用通过 cookie 的会话状态保持,false 表示不允许将 sessionID(实际上是 JSESSIONID)放到 cookie 中。
  • sessionManager.sessionIdUrlRewritingEnabled:是否允许将 sessionID 放到 URL 中,以 URL 参数的方式进行传递,当sessionManager.sessionIdCookieEnabled 设置为 true 时该项可以设为 false。
  • loginUrl 参数用来指定登录页面,当用户访问没有权限的 URL 时将自动跳转到该页面。

4.3 实现ACSRealm

Realm 是 Shiro 的核心组件,是用户身份信息,权限以及角色信息的提供者,从系统数据源,例如mysql中获取数据,然后转交给 Shiro 进行身份认证和权限管理,开发中一般基于其抽象子类 AuthorizingRealm 进行扩展。

创建config包,在该包下创建ACSRealm.java类,ACSRealm 将继承 AuthorizingRealm 。AuthorizingRealm 定义了两个抽象方法:doGetAuthorizationInfo 和 doGetAuthenticationInfo,这两个方法是 Shiro 进行身份认证和授权的关键。

认证

@Component
public class ACSRealm extends AuthorizingRealm {@Autowiredprivate SysUserMapper sysUserMapper;/*** 认证* @param token* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username = (String) token.getPrincipal();SysUser user = sysUserMapper.findByUsername(username);if (user == null) {throw new UnknownAccountException();}String pwd = user.getPassword();String password = new String(((char[]) token.getCredentials()));if (!pwd.equals(password)) {throw new IncorrectCredentialsException();}return new SimpleAuthenticationInfo(username, pwd, getName());}
}
    /*** 通过用户名查询用户* @param username* @return*/SysUser findByUsername(String username);
  <select id="findByUsername" resultType="com.picacho.springbootshiro.entity.SysUser">select<include refid="Base_Column_List"/>from sys_userwhere username = #{username}</select>

授权

@Component
public class ACSRealm extends AuthorizingRealm {@Autowiredprivate SysUserMapper sysUserMapper;/*** 授权* @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();Collection<Permission> permissions = loadPermissions(principals);authorizationInfo.addObjectPermissions(permissions);return authorizationInfo;}private Collection<Permission> loadPermissions(PrincipalCollection principals) {String username = (String) principals.getPrimaryPrincipal();List<String> ps = sysUserMapper.findAllPermissions(username);if (CollectionUtils.isEmpty(ps)) {return null;}return ps.stream().map(WildcardPermission::new).collect(Collectors.toList());}
}
    /*** 通过用户名查询权限* @param username* @return*/List<String> findAllPermissions(String username);
  <select id="findAllPermissions" resultType="java.lang.String">SELECT sp.permission_codeFROM sys_user suLEFT JOIN sys_user_role sur ON su.id = sur.user_idLEFT JOIN sys_role_permission srp ON sur.role_id = srp.role_idLEFT JOIN sys_permission sp ON srp.permission_id = sp.idWHERE username = #{username}</select>

4.4 配置 SecurityManager 和过滤器

SecurityManager 的配置比较简单,主要是将我们自己的 ACSRealm 配置给 SecurityManager。在config包下创建ShiroConfig.java类。

@Configuration
public class ShiroConfig {@BeanDefaultWebSecurityManager securityManager(ACSRealm realm) {DefaultWebSecurityManager manager = new DefaultWebSecurityManager();manager.setRealm(realm);return manager;}
}

接下来需要配置 Shiro 过滤器,Shiro 过滤器可以指定对 URL 路径应用什么样的访问控制策略,如控制某一 URL 允许匿名访问,或者需要完成身份认证(登录)后才能访问等。

    @BeanShiroFilterChainDefinition shiroFilterChainDefinition() {DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();// swagger 文档相关的接口允许匿名访问chainDefinition.addPathDefinition("/swagger**", "anon");chainDefinition.addPathDefinition("/webjars**", "anon");// 登录接口允许匿名访问chainDefinition.addPathDefinition("/api/login", "anon");// 所有 api 开头的接口都需要登录chainDefinition.addPathDefinition("/api/**", "authc");return chainDefinition;}

Shiro 的过滤器都定义在 org.apache.shiro.web.filter 包中,有唯一的简写字符串与之对应,常见的过滤器有下面几种:

  • anon:允许匿名访问。
  • authc:需要完成身份认证(登录)后才能访问。
  • perms:可指定访问者必须具备的权限,如:“perms[role:add, user:list]”,表示访问者需要同时具备 “role:add” 和 “user:list” 权限才能访问当前路径。
  • user:必须存在用户,身份认证通过或通过“记住我”认证通过的都可以访问。

4.5 简单测试一下

创建controller包,并在该包下创建LoginController类中,添加跳转至login.html的方法。

@RestController
public class LoginController {@GetMapping("/login.html")public String loginPage() {return "<p><h1>" +"模拟登录界面" +"</h1><h5>" +"有两种情况该页面被打开:" +"</h5><ul><li>" +"用户主动打开" +"</li><li>" +"无权限时自动跳转" +"</li></ul></p>";}
}

测试一下:访问此路径http://localhost:8080/api/hello,由于没有权限会被映射到http://localhost:8080/login.html路径,因此会出现如下画面。


demo源码下载地址:demo源码下载

Spring Boot与Shiro实现权限管理02相关推荐

  1. 如何在 Spring Boot 中用 Shiro 实现权限管理

    本行 Chat 内容包括: 简单介绍 IntelliJ IDEA 的安装与注册 什么是 Spring Boot? 如何通过 IDEA 创建一个简单的 Spring Boot 项目 Apache Shi ...

  2. Spring Boot + Mybatis + Shiro 后台权限管理系统

    平台简介 一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适的.于是利用空闲休息时间开始自己写了一套后台系统.如此有了若依.她可以用于所有的Web应用程序,如网站管理后台,网站会员中心, ...

  3. spring boot结合shiro实现用户-角色-权限的控制(包含用户名密码登陆和手机号验证码登陆)

    spring boot整合shiro实现权限校验 1.首先导入项目所需jar包 <parent><groupId>org.springframework.boot</gr ...

  4. spring boot整合shiro继承redis_spring-boot-plus集成Shiro+JWT权限管理

    SpringBoot+Shiro+JWT权限管理 Shiro Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理. 使用Shiro的易于理解的API,您可以 ...

  5. Spring Boot整合Shiro + Springboot +vue

    目录 02 Spring Boot整合Shiro p1.shiro概述 1 什么是Shiro 2 Shiro核心组件 p2.Shiro实现登录认证 AccountRealm.java QueryWra ...

  6. 六、Spring Boot整合Shiro

    六.Spring Boot整合Shiro 6.1.整合思路 6.2.创建spring boot项目 6.3.引入shiro依赖 6.4.配置shiro环境 创建配置类ShiroConfig 1.配置: ...

  7. 有手就行的 Spring Boot 集成 Shiro

    前言   Apache Shiro 是 Java 的一个安全框架.目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Se ...

  8. 《SpringBoot与Shiro整合-权限管理实战---从构建到模拟数据库登入》

    <SpringBoot与Shiro整合-权限管理实战> ---- 从构建到模拟数据库登入 ---- 点击下载源码 ---- 或者查看? 文章目录 <SpringBoot与Shiro整 ...

  9. 解决Spring Boot集成Shiro,配置类使用Autowired无法注入Bean问题

    为什么80%的码农都做不了架构师?>>>    如题,最近使用spring boot集成shiro,在shiroFilter要使用数据库动态给URL赋权限的时候,发现 @Autowi ...

最新文章

  1. Mysql sql优化(一)
  2. Confluence 6 配置服务器基础地址
  3. python dataframe loc函数_python pandas.DataFrame.loc函数使用详解
  4. java事件大全_Java sctipt常用事件汇总介绍
  5. 余弦信号频谱表达式_2019年清华828信号与系统试题回忆
  6. python抽象类的实现_python 抽象类、抽象方法的实现
  7. Mybatis主线流程源码解析
  8. 数据结构与算法分析(二)——C++实现链表
  9. 一个简单的samba案例(测试与思考)
  10. 机械硬盘旋转时间_高端PC真的没有机械盘了么?
  11. 多线程模拟实现生产者/消费者模型 (借鉴)
  12. SparkStreaming读取本地文件进行wordCount
  13. 【ACL2020论文尝鲜】何时采用BERT更加有效?
  14. 2019年春节加班机暴增 人潮多考验桃园机场准点率
  15. 22 个最常用的Python包
  16. 嵌入式系统开发笔记17:CJ/T-188 冷热量表协议解析6
  17. 笔记 | 制作windows10装机U盘,换固态硬盘,加内存条
  18. 关于解决Windows系统许可证即将过期的问题
  19. L2-037 包装机
  20. matplotlib 点线动画

热门文章

  1. 盒模型和开发中常用的布局
  2. 嵌入式linux技能,学IT技能 学嵌入式Linux必知内容
  3. java用代码实现星期菜谱_基于JAVA的菜谱大全接口调用代码实例
  4. 宁静、万茜等姐姐们“乘风破浪”,各品牌借势掀起新一波火热营销
  5. web端腾讯PAG初体验
  6. 软件测试行业就业前景到底怎么样?
  7. 淘宝/天猫获得淘宝店铺详情 API接口及 返回值说明
  8. 斯坦福大学秋季课程《深度学习理论》STATS 385开讲
  9. 皇帝踏入31 克城冠军梦 时不我待
  10. `英语` 2022/8/18