全程配图超清晰的Springboot权限控制后台管理项目实战第二期(Springboot+shiro+mybatis+redis)
众所周知,作为一个后端新手学习者,通过项目来学习,增长项目经验,是一个非常好的学习途径,所以我就找到了一个个人博客的项目,经过自己的调试和学习,现在已经比较完全的掌握了这个项目的创作过程,和一些细节方面的东西,在这里我把项目源码和项目实例都给出来,并且在后面进行一些细节和整体思路上的详解。
Springboot加Springsceurity实现权限管理系统
Springboot整合shiro
众所周知,shiro和springsecurity作为两个著名的框架,被人们广泛使用,当然这两个框架各有利弊,shiro是一个相对来说简单一点,好理解一点,所以大部分的项目都会使用这个框架进行安全认证,个人认为也是比较易上手的一个简单框架,但是SpringSecurity作为一个Spring家族的一员,是一个非常成熟而且功能强大的安全框架,在这里我们就来详细的介绍一下shiro的内容,源码也会在我的gitee仓库中,是一个比较好看的个人博客,欢迎大家来进行点评。
shiro
shiro整体思路
- shiro作为一个轻量级,易管理的安全框架,底层逻辑必然是学习必不可少的一个环节
- Subject
主体,代表当前‘用户’ 。这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委派给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者。 - Principal
身份信息,是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal) - Shiro SecurityManager
安全管理器;即所有与安全有关的操作都会与SecurityManager交互且它管理者所有Subject;可以看出它是Shiro的核心,它负责与后面介绍的其它组件进行交互,可以把它看成DispathcherServlet前端控制器 - Realm
数据域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源 - 专有名词
springboot整合mybatis链接数据库
- 导入mybatis的逆向工程,通过数据库的表数据来导入逆向工程生成entity,mapper,service,还有controller等类,这些类是不需要自己进行写的,毕竟是一些重复而没有太多技术要求的工作,因为一个项目可能会有很多的数据库,然后如果自己写的话,会花费很长时间,所以我们通过mybatis-plus的逆向工程来完成这一部分的内容。
package com.danli;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.FileType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotEmpty(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/vueblog/src/main/java");
// gc.setOutputDir("D:\\test");
gc.setAuthor("fanfanli");
gc.setOpen(false);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
gc.setServiceName("%sService");
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC");
// dsc.setSchemaName("public");b
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("1234");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(null);
pc.setParent("com.danli");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
cfg.setFileCreate((configBuilder, fileType, filePath) -> {
//如果是Entity则直接返回true表示写文件
if (fileType == FileType.ENTITY) {
return true;
}
//否则先判断文件是否存在
File file = new File(filePath);
boolean exist = file.exists();
if (!exist) {
file.getParentFile().mkdirs();
}
//文件不存在或者全局配置的fileOverride为true才写文件
return !exist || configBuilder.getGlobalConfig().isFileOverride();
});
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/vueblog/src/main/resources/mapper/"
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix("m_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
sql文件也在我的项目中,在此不做过多叙述。
- 然后在UserMapper中编写sql语句,详情如下
package com.danli.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.danli.common.lang.vo.UserInfo;
import com.danli.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author fanfanli
* @date 2021-04-08
*/
@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
/**
* 获取用户部分信息list
*/
@Select("select id, nickname, username, avatar, email, status, create_time, update_time, role from user order by create_time desc")
List<UserInfo> getUserInfo();
}
- 编写Userservice类以及Userserviceimpl实现类,具体实现代码如下
package com.danli.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.danli.common.lang.v