# springboot-shiro
**Repository Path**: ilovemo/springboot-shiro
## Basic Information
- **Project Name**: springboot-shiro
- **Description**: springboot-shiro集成
- **Primary Language**: Java
- **License**: GPL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-01-04
- **Last Updated**: 2021-01-04
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 1、认识Shiro
> Shiro简介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
- Shiro是一个开源的java安全(权限)框架,它能够实现身份验证、授权、加密和会话管理等功能。
- Shiro是Apache旗下的产品,它的官网是: [Shiro官网: Apache Shiro](http://shiro.apache.org/index.html)
- Shiro不依赖任何容器,不仅可以运行在JavaEE环境,也可以运行在JavaSE
> Shiro 核心组件
用户、角色、权限
给角色赋予权限,给用户赋予角色
1、UsernamePasswordToken:Shiro 用来封装用户登录信息,使用用户的登录信息来创建令牌 Token。
2、SecurityManage:Shiro 的核心部分,负责安全认证和授权。
3、Suject:Shiro 的一个抽象概念,包含了用户信息。
4、Realm:开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部写在 Realm 中。
5、AuthenticationInfo:用户的角色信息集合,认证时使用。
6、AuthorzationInfo:角色的权限信息集合,授权时使用。
7、DefaultWebSecurityManager:安全管理器,开发者自定义的 Realm 需要注入到 DefaultWebSecurityManager 进行管理才能生效。
8、ShiroFilterFactoryBean:过滤器工厂,Shiro 的基本运行机制是开发者定制规则,Shiro 去执行,具体的执行操作就是由 ShiroFilterFactoryBean 创建的一个个 Filter 对象来完成。
Shiro 的运行机制如下图所示:

# 2、SpringBoot 整合 Shiro(单机)
1、创建 Spring Boot 应用作为父工程,pom.xml如下
```xml
4.0.0
pom
shiro-thymeleaf
shiro-jwt
org.springframework.boot
spring-boot-starter-parent
2.4.1
com.godfrey
springboot-shiro
0.0.1-SNAPSHOT
springboot-shiro
Demo project for Spring Boot
1.8
3.4.1
1.7.0
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
mysql
mysql-connector-java
com.baomidou
mybatis-plus-boot-starter
${mybatis-plus.version}
org.apache.shiro
shiro-spring
${shiro-spring.version}
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
```
2、创建子模块shiro-thymeleaf,pom.xml如下
```xml
springboot-shiro
com.godfrey
0.0.1-SNAPSHOT
4.0.0
shiro-thymeleaf
2.0.0
org.springframework.boot
spring-boot-starter-thymeleaf
com.github.theborakompanioni
thymeleaf-extras-shiro
${thymeleaf-shiro.version}
```
3、数据库SQL脚本SpringBoot-Shiro.sql
```sql
CREATE DATABASE IF NOT EXISTS `test`;
USE `test`;
CREATE TABLE IF NOT EXISTS `account` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`password` varchar(20) DEFAULT NULL,
`perms` varchar(20) DEFAULT NULL,
`role` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `account` (`id`, `username`, `password`, `perms`, `role`) VALUES
(1, 'zs', '123123', '', ''),
(2, 'ls', '123123', 'manager', ''),
(3, 'ww', '123123', 'manager', 'administrator');
```
3、配置文件,application.yml
```yml
# 项目相关配置
spring:
datasource:
# 数据库驱动
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据库连接url
url: jdbc:mysql://39.106.41.184:3306/test?userSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
# 用户名
username: root
# 密码
password: root123
# Thymeleaf配置
thymeleaf:
# 前缀
prefix: classpath:/templates/
# 后缀
suffix: .html
# 是否缓存
cache: false
# mybatis-plus配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
```
4、MybatisPlus配置类
```java
package com.godfrey.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
/**
* MybatisPlus配置
*
* @author godfrey
* @since 2020-1-2
*/
@Configuration
@MapperScan("com.godfrey.mapper")
public class MybatisPlusConfig {
}
```
5、实体类
```java
package com.godfrey.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @author godfrey
* @since 2021-01-02
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("account")
public class Account {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 权限
*/
private String perms;
/**
* 角色
*/
private String role;
}
```
6、mapper接口
```java
package com.godfrey.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.godfrey.entity.Account;
/**
* @author godfrey
* @since 2021-01-02
*/
public interface AccountMapper extends BaseMapper {
}
```
7、测试数据库能否连通
```java
package com.godfrey.mapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author godfrey
* @since 2021-01-02
*/
@SpringBootTest
class AccountMapperTest {
@Autowired
private AccountMapper mapper;
@Test
void test() {
mapper.selectList(null).forEach(System.out::println);
}
}
```
8、服务接口
```java
package com.godfrey.service;
import com.godfrey.entity.Account;
/**
* @author godfrey
* @since 2021-01-02
*/
public interface AccountService {
Account findByUserName(String userName);
}
```
9、服务实现
```java
package com.godfrey.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.godfrey.entity.Account;
import com.godfrey.mapper.AccountMapper;
import com.godfrey.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author godfrey
* @since 2021-01-02
*/
@Service
public class AccountServiceImpl implements AccountService {
private final AccountMapper accountMapper;
@Autowired
public AccountServiceImpl(AccountMapper accountMapper) {
this.accountMapper = accountMapper;
}
@Override
public Account findByUserName(String username) {
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
return accountMapper.selectOne(wrapper);
}
}
```
10、测试Service是否有误
```java
package com.godfrey.service;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author godfrey
* @since 2021-01-02
*/
@SpringBootTest
class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
void findByUserName() {
System.out.println(accountService.findByUserName("ls"));
}
}
```
11、自定义 Shiro 过滤器AccountRealm
```java
package com.godfrey.realm;
import com.godfrey.entity.Account;
import com.godfrey.service.AccountService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.Set;
/**
* @author godfrey
* @since 2021-01-02
*/
public class AccountRealm extends AuthorizingRealm {
@Autowired
private AccountService accountService;
/**
* 授权
*
* @param principalCollection
* @return org.apache.shiro.authz.AuthorizationInfo
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前登录的用户信息
Subject subject = SecurityUtils.getSubject();
Account account = (Account) subject.getPrincipal();
//设置角色
Set roles = new HashSet<>();
roles.add(account.getRole());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//设置权限
info.addStringPermission(account.getPerms());
return info;
}
/**
* 认证
*
* @param authenticationToken
* @return org.apache.shiro.authc.AuthenticationInfo
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
Account account = accountService.findByUserName(token.getUsername());
if (null != account) {
return new SimpleAuthenticationInfo(account, account.getPassword(), getName());
}
return null;
}
}
```
12、Shiro配置类
```java
package com.godfrey.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.godfrey.realm.AccountRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author godfrey
* @since 2021-01-02
*/
@Configuration
public class ShiroConfig {
/**
* 第一步:创建Realm对象
*
* @param
* @return com.godfrey.realm.AccountRealm
*/
@Bean
public AccountRealm accountRealm() {
return new AccountRealm();
}
/**
* 第二步:创建DefaultWebSecurityManager,Realm对象放入DefaultWebSecurityManager
*
* @param accountRealm
* @return org.apache.shiro.web.mgt.DefaultWebSecurityManager
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("accountRealm") AccountRealm accountRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(accountRealm);
return securityManager;
}
/**
* 第三步:创建ShiroFilterFactoryBean,DefaultWebSecurityManager对象放入hiroFilterFactoryBean
*
* @param securityManager
* @return org.apache.shiro.spring.web.ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
//权限设置
Map filterMap = new LinkedHashMap<>();
filterMap.put("/main","authc");
filterMap.put("/manager","perms[manager]");
filterMap.put("/administrator","roles[administrator]");
factoryBean.setFilterChainDefinitionMap(filterMap);
//设置登录页面
factoryBean.setLoginUrl("/login");
//设置未授权页面
factoryBean.setUnauthorizedUrl("/unauth");
return factoryBean;
}
/**
* ShiroDialect(方言):shiro整合thymeleaf
*
* @param
* @return at.pollux.thymeleaf.shiro.dialect.ShiroDialect
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
```
13、搭建三个页面(main.html,manager.html,administrator.html)
```html
Title
main
```
```html
Title
manager
```
```html
Title
administrator
```
14、编写controller
```java
package com.godfrey.controller;
import com.godfrey.entity.Account;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
/**
* @author godfrey
* @since 2021-01-03
*/
@Controller
public class AccountController {
@GetMapping("/{url}")
public String redirect(@PathVariable("url") String url) {
return url;
}
@PostMapping("/login")
public String login(String username, String password, Model model) {
//获取当前用户
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//去到Realm认证方法
try {
subject.login(token);
Account account = (Account) subject.getPrincipal();
subject.getSession().setAttribute("account", account);
return "index";
} catch (UnknownAccountException e) {
e.printStackTrace();
model.addAttribute("msg", "用户名错误!");
return "login";
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
model.addAttribute("msg", "密码错误!");
return "login";
}
}
@GetMapping("/unauth")
@ResponseBody
public String unauth() {
return "未授权无法访问此页面!";
}
@GetMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "login";
}
}
```
测试三个页面访问
15、编写首页(index.html)、登录页(login.html)
```html
Title
index
main
```
```html
Title
```
访问权限如下:
1、必须登录才能访问 main.html
2、当前用户必须拥有 manage 授权才能访问 manage.html
3、当前用户必须拥有 administrator 角色才能访问 administrator.html
至此,整合完成!!!
> 认证过滤器
anon:无需认证。
authc:必须认证。
authcBasic:需要通过 HTTPBasic 认证。
user:不一定通过认证,只要曾经被 Shiro 记录即可,比如:记住我。
> 授权过滤器
perms:必须拥有某个权限才能访问。
role:必须拥有某个角色才能访问。
port:请求的端口必须是指定值才可以。
rest:请求必须基于 RESTful,POST、PUT、GET、DELETE。
ssl:必须是安全的 URL 请求,协议 HTTPS。
# 3、SpringBoot 整合 Shiro+Jwt(前后端分离)