Spring Boot整合Shiro
1.Shiro
什么是Shiro
是一款主流的Java安全框架,不依赖任何容器,可以运行在Java SE和Java EE项目中,它的主要作用是对访问系
统的用户进行身份认证、授权、会话管理、加密等操作。
Shiro就是用来解决安全管理的系统化框架。
2 Shiro核心组件
用户、角色、权限
会给角色赋予权限,给用户赋予角色
1、UsernamePasswordToken, Shiro 用来封装用户登录信息,使用用户的登录信息来创建令牌Token。
2、SecurityManager, Shiro 的核心部分,负责安全认证和授权。
3、Suject, Shiro 的一个抽象概念,包含了用户信息。
4、Realm,开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部写在Realm中。
5、AuthenticationInfo, 用户的角色信息集合,认证时使用。
6、AuthorzationInfo, 角色的权限信息集合,授权时使用。
7、DefaultWebSecurityDManager, 安全管理器,开发者自定义的Realm需要注入到
DefaultWebSecurityDManager进行管理才能生效。
8、ShiroFilterFactoryBean, 过滤器工厂,Shiro 的基本运行机制是开发者定制规则,Shiro 去执行,具体的执行操
作就是由ShiroFilterFactoryBean创建的一个个Filter对象来完成。
Shiro的运行机制如下图所示。
Shiro的核心概念Subject、SecurityManager、Realm
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如爬虫、机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者。
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是shiro的核心, SecurityManager相当于spring mvc中的dispatcherServlet前端控制器。
Realm:域,shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
3.Spring Boot整合Shiro
项目开始:
一、数据库设计
二、创建项目导入依赖(springboot项目为例)
1、用idea新建Spring Initializr项目,项目结构如下:
2、添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</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>
</dependencies>
2.配置application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/shiro?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&allowPublicKeyRetrieval=true
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
private Integer id;
private String username;
private String password;
private String perms;
private String role;
}
类介绍:
在Realm类中去写认证逻辑和授权逻辑;
public class AccountRealm extends AuthorizingRealm {
@Autowired
private AccountService accountService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取当前登录的用户信息
Subject subject = SecurityUtils.getSubject();
Account account = (Account) subject.getPrincipal();
//设置角色
Set<String> roles = new HashSet<>();
roles.add(account.getRole());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//设置权限
info.addStringPermission(account.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
Account account = accountService.findByUsername(token.getUsername());
if (account != null){
return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
}
return null;
}
}
写完之后,配置一个config类,配置类里面需要注入ShiroFilterFactoryBean,securityManager,Realm,配置完这3个接下来就可以写你的逻辑了;
ShiroFilterFactoryBean:是个拦截器,在请求进入控制层前将其拦截,需要将安全管理器SecurityManager注入其中。
SecurityManager:安全管理器,需要将自定义realm注入其中,以后还可以将缓存、remeberme等注入其中
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
//权限设置
Map<String,String> map = new Hashtable<>();
map.put("/main","authc");
map.put("/manage","perms[manage]");
map.put("/administrator","roles[administrator]");
factoryBean.setFilterChainDefinitionMap(map);
//设置登录页面
factoryBean.setLoginUrl("/login");
//设置未授权页面
factoryBean.setUnauthorizedUrl("/unauth");
return factoryBean;
}
@Bean
public DefaultWebSecurityManager securityManager(@Qualifier("accountRealm") AccountRealm accountRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(accountRealm);
return manager;
}
@Bean
public AccountRealm accountRealm(){
return new AccountRealm();
}
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
Controller类说明
登录过程其实只是处理异常的相关信息,具体的登录验证交给shiro来处理
@Controller
@Slf4j
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){
log.warn("username is ======>"+username);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
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){
model.addAttribute("msg","密码错误!");
e.printStackTrace();
return "login";
}
}
@GetMapping("/unauth")
@ResponseBody
public String unauth(){
return "未授权,无法访问:";
}
@GetMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "login";
}
}
页面说明
登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="shortcut icon" href="#"/>
</head>
<body>
<form action="/login" method="post">
<table>
<span th:text="${msg}" style="color: red"></span>
<tr>
<td>用户名:</td>
<td>
<input type="text" name="username"/>
</td>
</tr>
<tr>
<td>密码:</td>
<td>
<input type="password" name="password"/>
</td>
</tr>
<tr>
<td>
<input type="submit" value="登录"/>
</td>
</tr>
</table>
</form>
</body>
</html>
index页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="shortcut icon" href="#"/>
</head>
<body>
<h1>index</h1>
<div th:if="${session.account != null}">
<span th:text="${session.account.username}+'欢迎回来!'"></span><a href="/logout">退出</a>
</div>
<a href="/main">main</a> | <a href="manage">manage</a> | <a href="/administrator">administrator</a>
<!--<a href="/main">main</a> <br/>-->
<!--<div shiro:hasPermission="manage">-->
<!--<a href="manage">manage</a> <br/>-->
<!--</div>-->
<!--<div shiro:hasRole="administrator">-->
<!--<a href="/administrator">administrator</a>-->
<!--</div>-->
</body>
</html>
administator.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>administator</h1>
</body>
</html>
main.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>main</h1>
</body>
</html>
manage.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>manage</h1>
</body>
</html>
编写认证和授权规则:
认证过滤器
auths:必须认证
authcBasic:需耍通过HTTPBasic认证
user:不-定通过认证,只要曾经被Shiro记录即可,比如:记住我
授权过滤器
perms:必须拥有某个权限才能访问
role:必须拥有某个角色才能访问
port:请求的端口必须是指定值才可以
rest:请求必须基于RESTful, POST. PUT. GET. DELETE
ssl:必须是安全的URL请求,协议HTTPS
创建3个页面,main.html. manage.html. administrator.html
访问权限如下:
1、必须登录才能访问main.html
2、当前用户必须拥有manage授权才能访问manage.html
3、当前用户必须拥有administrator角色才能访问administrator.html
Shiro整合Thymeleaf
1.pom.xml 引入依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2.配置类添加 ShiroDialect
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
Service类
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public Account findByUsername(String username) {
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("username",username);
return accountMapper.selectOne(wrapper);
}
}
public interface AccountService {
public Account findByUsername(String username);
}
mapper类
public interface AccountMapper extends BaseMapper<Account> {
}
测试类
@SpringBootTest
class AccountMapperTest {
@Autowired
private AccountMapper mapper;
@Test
void test(){
mapper.selectList(null).forEach(System.out::println);
}
}
@SpringBootTest
class SpringbootShiroApplicationTests {
@Test
void contextLoads() {
}
}
@SpringBootApplication
@MapperScan("com.fang.mapper")
public class SpringbootShiroApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootShiroApplication.class, args);
}
}
测试
http://localhost:8080/login 页面
登录成功跳转 index 页面
本文地址:https://blog.csdn.net/liyanfang0310/article/details/107163379
推荐阅读
-
Spring Boot Dubbo 构建分布式服务的方法
-
Spring Boot实现STOMP协议的WebSocket的方法步骤
-
深入理解spring boot异步调用方式@Async
-
详解Spring Boot配置使用Logback进行日志记录的实战
-
详解Spring Boot实战之Filter实现使用JWT进行接口认证
-
spring boot静态变量注入配置文件详解
-
详解spring boot jpa整合QueryDSL来简化复杂操作
-
Spring Boot 配置文件详解(小结)
-
spring boot整合Cucumber(BDD)的方法
-
使用Spring Boot集成FastDFS的示例代码