文章目录
源码地址: link
一、添加依赖
<?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.joecy</groupId>
<artifactId>springboot_mybatis-plus_shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot_mybatis-plus_shiro</name>
<description>Spring Boot整合Mybitis Plus和Shiro小Demo</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<mybatisplus-spring-boot-starter.version>1.0.5</mybatisplus-spring-boot-starter.version>
<mybatisplus.version>2.1.4</mybatisplus.version>
<shiro-spring.version>1.3.2</shiro-spring.version>
<druid.version>1.0.18</druid.version>
<lombok.version>1.18.2</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--没有此依赖shiro注解权限会失效-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--lombok插件使用依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!--mybatis plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatisplus-spring-boot-starter</artifactId>
<version>${mybatisplus-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
<!--数据源相关-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--shiro依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro-spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
</dependencies>
</project>
二、application.properties配置文件
server.port=8080
# 数据库访问配置
# 主数据源,默认的
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus_shiro_test?Unicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
#spring.datasource.useGlobalDataSourceStat=true
#集成Freemark
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true
spring.freemarker.request-context-attribute=request
spring.freemarker.allow-session-override=true
#Mybatis配置
mybatis-plus.mapper-locations=classpath:mappers/*.xml
#mybatis-plus的model包
mybatis-plus.typeAliasesPackage=com.joecy.entity
#global-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
#id-type: 2
mybatis-plus.global-config.id-type=0
#驼峰式命名映射
mybatis-plus.global-config.db-column-underline=true
三、mybatis-plus相关
1 新建数据库表对应的model,以SecUser为例
/**
* 用户表
* 需要继承mybatis-plus的Model<>抽象类
*/
@Data
public class SecUser extends Model<SecUser> {
//指定id
@TableId
private Integer id;
private String username;
private String password;
@TableField(exist = false)
private List<SecRole> roles;
//实现默认id方法
//若一个表没有id 直接return null即可
@Override
protected Serializable pkVal() {
return this.id;
}
}
2 创建SecUser对应Mapper SecUserMapper
//必须添加mybatis的@Mapper注解
@Mapper
public interface SecUserMapper extends BaseMapper<SecUser> {
}
至此该model就可以正常使用了,具体使用方法很简单,参照mybatis-plus官网:link
四、现在开始Shiro配置
1 创建MyShiroRealm
//实现AuthorizingRealm接口用户用户认证
public class MyShiroRealm extends AuthorizingRealm {
//角色权限和对应权限添加
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
System.out.println(principalCollection.getPrimaryPrincipal());
SecUser user= (SecUser) principalCollection.getPrimaryPrincipal();
//保存所有角色名
Set<String> allRoles = new HashSet<>();
//保存所有权限名
Set<String> allPermissions = new HashSet<>();
//查询对应角色
List<SecUserRole> secUserRoles = new SecUserRole().selectList(new EntityWrapper().eq("user_id", user.getId()));
for (SecUserRole userRole:secUserRoles) {
SecRole role = new SecRole();
role.setId(userRole.getRoleId());
role = role.selectById();
allRoles.add(role.getName());
//查询所有权限
List<SecPermission> permissions = new ArrayList<>();
List<SecRolePermission> rolePermissions = new SecRolePermission().selectList(new EntityWrapper().eq("role_id", role.getId()));
for (SecRolePermission rolePermission:rolePermissions) {
SecPermission permission = new SecPermission();
permission.setId(rolePermission.getPermissionId());
permission = permission.selectById();
allPermissions.add(permission.getName());
}
}
//添加角色
simpleAuthorizationInfo.addRoles(allRoles);
simpleAuthorizationInfo.addStringPermissions(allPermissions);
System.out.println(user);
return simpleAuthorizationInfo;
}
//用户认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 1、登录认证的方法需要先执行,需要用他来判断登录的用户信息是否合法
String username = (String) token.getPrincipal(); // 取得用户名
// 需要通过用户名取得用户的完整信息,利用业务层操作
SecUser user = null;
try {
user = new SecUser().selectOne(new EntityWrapper().eq("username",username));
} catch (Exception e) {
e.printStackTrace();
}
if (user == null) {
throw new UnknownAccountException("该用户名称不存在!");
} else { // 进行密码的验证处理
String password =new String((char[]) token.getCredentials());
// 将数据库中的密码与输入的密码进行比较,这样就可以确定当前用户是否可以正常登录
if (user.getPassword().equals(password)) { // 密码正确
AuthenticationInfo auth = new SimpleAuthenticationInfo(user, password, "memberRealm");
return auth;
} else {
throw new IncorrectCredentialsException("密码错误!");
}
}
}
}
2 创建Shiro配置类ShiroConfig
@Configuration
public class ShiroConfig {
//将自己的验证方式加入容器
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
return myShiroRealm;
}
//权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
// 权限控制map.
LinkedHashMap<String, String> filterChainDefinitionMap=new LinkedHashMap<>();
filterChainDefinitionMap.put("/css/**", "anon"); //表示可以匿名访问
filterChainDefinitionMap.put("/js/**", "anon"); //表示可以匿名访问
filterChainDefinitionMap.put("/img/**", "anon"); //表示可以匿名访问
filterChainDefinitionMap.put("/font/**", "anon"); //表示可以匿名访问
filterChainDefinitionMap.put("/images/**", "anon"); //表示可以匿名访问
filterChainDefinitionMap.put("/login", "anon"); //表示可以匿名访问
filterChainDefinitionMap.put("/user_login", "anon");
filterChainDefinitionMap.put("/logout*","logout");
filterChainDefinitionMap.put("/error","anon");
filterChainDefinitionMap.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
//加入注解的使用,不加入这个注解不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
System.out.println("开启了shiro注解功能");
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
3 创建Shiro权限不足处理类ExceptionConf
/**
* @author 陈玉林
* 该类配置Shiro未授权跳转
*/
@Configuration
public class ExceptionConf {
@Bean
public SimpleMappingExceptionResolver resolver() {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/403");
resolver.setExceptionMappings(properties);
return resolver;
}
}
五、创建LoginController
@Controller
public class LoginController {
@GetMapping(value = "/login")
public String login(){
return "login";
}
@PostMapping(value = "/user_login")
public String login(@RequestParam String username, @RequestParam String password, Model model){
try {
model.addAttribute("username", username);
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
//完成登录
subject.login(usernamePasswordToken);
return "redirect:/index";
} catch (Exception e) {
String ex = e.getClass().getName();
if (ex != null) {
if (UnknownAccountException.class.getName().equals(ex)) {
System.out.println("用户名不存在");
} else if (IncorrectCredentialsException.class.getName().equals(ex)) {
System.out.println("账户或密码错误");
} else {
System.out.println("未知错误");
}
}
//返回登录页面
return "login";
}
}
@ResponseBody
@GetMapping(value = "/index")
public String index(){
return "welcome";
}
@RequestMapping("/logout")
public String logOut(HttpSession session) {
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/login";
}
//未授权mapping配置
@ResponseBody
@GetMapping(value = "/403")
public String error(){
return "没有权限!";
}
//注解的使用
//管理员角色
@ResponseBody
@RequiresRoles("admin")
@RequestMapping(value = "/role/admin")
public String roleAdmin(){
return "I am admin";
}
//普通用户角色
@ResponseBody
@RequiresRoles("common")
@RequestMapping(value = "/role/common")
public String roleCommon(){
return "I am common";
}
//拥有添加、删除权限
@ResponseBody
@RequiresPermissions({"add","delete"})
@RequestMapping(value = "/permissions")
public String Permissions(){
return "I have permissions add and delete";
}
//只拥有添加权限
@ResponseBody
@RequiresPermissions("add")
@RequestMapping(value = "/permission/add")
public String Permission(){
return "I have permission add";
}
}
六、创建一个登录界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form action="/user_login" method="post">
<input type="text" name="username" placeholder="用户名" >
<input type="password" name="password" placeholder="密码" >
<input type="submit" value="提交">
</form>
</body>
</html>