安全管理是软件系统必不可少的的功能。根据经典的“墨菲定律”——凡是可能,总会发生。如果系统存在安全隐患,最终必然会出现问题,这篇文章主要介绍了SpringBoot安全管理Shiro框架的使用
Shiro 简介
Apache Shiro 是一个开源的轻量级的 Java 安全框架,它提供身份验证、授权、密码管理以及会话管理等功能。相对于 Spring Security ,Shiro 框架更加直观、易用,同时也能提供健壮的安全性。
在传统的 SSM 框架中,手动整合 Shiro 的配置步骤还是比较多的,针对 Spring Boot ,Shiro 官方提供了 shiro-spring-boot-web-starter 用来简化 Shiro 在 Spring Boot 中的配置。
整合 Shiro
1. 创建项目
首先创建一个普通的 Spring Boot Web 项目,添加 Shiro 依赖以及页面模板依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
这里不需要添加 spring-boot-starter-web 依赖,shiro-spring-boot-web-starter 中已经依赖了 spring-boot-starter-web 。同时,此处使用 Thymeleaf 模板,为了在 Thymeleaf 使用 shiro 标签,加入了 thymeleaf-extras-shiro 依赖。
2. Shiro基本配置
在 application.properties 中配置 Shiro 的基本信息
# 开启 Shiro 配置,默认为 true
shiro.enabled=true
# 开启 Shiro Web 配置,默认为 true
shiro.web.enabled=true
# 配置登录地址,默认为 /login.jsp
shiro.loginUrl=/login
# 配置登录成功的地址,默认为 /
shiro.successUrl=/index
# 未获授权默认跳转地址
shiro.unauthorizedUrl=/unauthorized
# 是否允许通过 URL 参数实现会话跟踪,如果网站支持 Cookie,可以关闭此选项,默认为 true
shiro.sessionManager.sessionIdUrlRewritingEnabled=true
# 是否允许通过 Cookie 实现会话跟踪,默认为 true
shiro.sessionManager.sessionIdCookieEnabled=true
然后在 Java 代码中配置 Shiro ,提供两个最基本的 Bean 即可
@Configuration
public class ShiroConfig {
@Bean
public Realm realm() {
TextConfigurationRealm realm = new TextConfigurationRealm();
realm.setUserDefinitions("sang=123,user\n admin=123,admin");
realm.setRoleDefinitions("admin=read,write\n user=read");
return realm;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition =
new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/login", "anon");
chainDefinition.addPathDefinition("/doLogin", "anon");
chainDefinition.addPathDefinition("/logout", "logout");
chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
代码解释:
- 这里提供两个关键的 Bean ,一个是 Realm,另一个是 ShiroFilterChainDefinition 。至于 ShiroDialect 则是为了支持在 Thymeleaf 中使用 Shiro 标签,如果不在 Thymeleaf 中使用 Shiro 标签,那么可以不提供 ShiroDialect
- Realm 可以是自定义的 Realm,也可以是 Shiro 提供的 Realm,简单起见,此处没有配置数据库连接,直接配置了两个用户:sang/123 和 admin/123 ,分别对应角色 user 和 admin。
- ShiroFilterChainDefinition Bean 中配置了基本的过滤规则 ,“/login” 和 “/doLogin”,可以匿名访问,“/logout”是一个注销登录请求,其余请求则都需要认证后才能访问
然后配置登录接口以及页面访问接口
@Controller
public class UserController {
@PostMapping("/doLogin")
public String doLogin(String username, String password, Model model) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
} catch (AuthenticationException e) {
model.addAttribute("error", "用户名或密码输入错误!");
return "login";
}
return "redirect:/index";
}
@RequiresRoles("admin")
@GetMapping("/admin")
public String admin() {
return "admin";
}
@RequiresRoles(value = {"admin", "user"}, logical = Logical.OR)
@GetMapping("/user")
public String user() {
return "user";
}
}
代码解释:
- 在 doLogin 方法中,首先构建一个 UsernamePasswordToken 实例,然后获取一个 Subject 对象并调用该对象中的 login 方法执行登录操作,在登录操作执行过程中,当有异常抛出时,说明登录失败,携带错误信息返回登录视图;当登录成功时,则重定向到“/index”
- 接下来暴露两个接口“/admin”和“/user”,对于“/admin”接口,需要具有 admin 角色才可以访问;对于“/user”接口,具备 admin 角色 和 user角色其中任意一个即可访问
对于其他不需要角色就能访问的接口,直接在 WebMvc 中配置即可
@Configuration
public class WebMvcConfig implements WebMvcConfigurer{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/unauthorized").setViewName("unauthorized");
}
}
接下来创建全局异常处理器进行全局异常处理,此处主要是处理授权异常
@ControllerAdvice
public class ExceptionController {
@ExceptionHandler(AuthorizationException.class)
public ModelAndView error(AuthorizationException e) {
ModelAndView mv = new ModelAndView("unauthorized");
mv.addObject("error", e.getMessage());
return mv;
}
}
当用户访问未授权的资源时,跳转到 unauthorized 视图中,并携带出错误信息。
配置完成后,最后在 resources/templates 目录下创建 5 个 HTML 页面进行测试。
(1)index.html
<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>Hello, <shiro:principal/></h3>
<h3><a href="/logout" rel="external nofollow" >注销登录</a></h3>
<h3><a shiro:hasRole="admin" href="/admin" rel="external nofollow" >管理员页面</a></h3>
<h3><a shiro:hasAnyRoles="admin,user" href="/user" rel="external nofollow" >普通用户页面</a></h3>
</body>
</html>
(2)login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<form action="/doLogin" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<div th:text="${error}"></div>
<input type="submit" value="登录">
</form>
</div>
</body>
</html>
(3)user.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>普通用户页面</h1>
</body>
</html>
(4)admin.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>管理员页面</h1>
</body>
</html>
(5)unauthorized.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h3>未获授权,非法访问</h3>
<h3 th:text="${error}"></h3>
</div>
</body>
</html>
3. 测试
启动项目,访问登录页面,使用 sang/123 登录
注意:由于 sang 用户不具备 admin 角色,因此登录成功后的页面没有前往管理员页面的超链接。
然后使用 admin/123 登录。
如果用户使用 sang 登录,然后去访问:http://localhost:8080/admin,会跳转到未授权页面
以上通过一个简单的案例展示了如何在 Spring Boot 中整合 Shiro 以及如何在 Thymeleaf 中使用 Shiro 标签,一旦整合成功,接下来 Shiro 的用法就和原来的一模一样。此处主要将 Spring Boot 整合 Shiro,对于 Shiro 的其它用法,可以参考 Shiro 官方文档。
到此这篇关于SpringBoot浅析安全管理之Shiro框架的文章就介绍到这了,更多相关SpringBoot Shiro框架内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!
本文标题为:SpringBoot浅析安全管理之Shiro框架
基础教程推荐
- JDK数组阻塞队列源码深入分析总结 2023-04-18
- Java并发编程进阶之线程控制篇 2023-03-07
- java实现多人聊天系统 2023-05-19
- java基础知识之FileInputStream流的使用 2023-08-11
- springboot自定义starter方法及注解实例 2023-03-31
- Java实现线程插队的示例代码 2022-09-03
- ConditionalOnProperty配置swagger不生效问题及解决 2023-01-02
- Java实现查找文件和替换文件内容 2023-04-06
- Java文件管理操作的知识点整理 2023-05-19
- Java数据结构之对象比较详解 2023-03-07