适合才最美:Shiro安全框架使用心得
大家好,我是 V 哥。Apache Shiro 是一个强大且灵活的 Java 安全框架,专注于提供认证、授权、会话管理和加密功能。它常用于保护 Java 应用的访问控制,特别是在 Web 应用中。相比于 Spring Security,Shiro 的设计更简洁,适合轻量级应用,并且在许多方面具有更好的易用性和扩展性,今天 V 哥就来聊聊 Shiro 安全框架。
Shiro 的核心概念
按照惯例,和 V 哥一起来了解一下 Shiro 的核心概念:
Subject
Subject 是 Shiro 框架中一个核心的接口,表示应用中的“用户”或“实体”,用于交互和存储认证状态。通常通过SecurityUtils.getSubject()获取当前的 Subject。它代表了用户的身份信息和权限数据。SecurityManager
SecurityManager 是 Shiro 的核心控制器,负责管理所有的安全操作和认证。通过配置 SecurityManager,可以控制用户的认证、授权、会话等管理。Realm
Realm 是 Shiro 从数据源获取用户、角色和权限信息的途径。通过实现自定义的 Realm,可以将 Shiro 与数据库、LDAP、文件等数据源整合。Shiro 会把用户的认证和授权数据从 Realm 中获取。Session
Shiro 自带会话管理,不依赖于 Servlet 容器提供的会话。即使在非 Web 环境下,也可以使用 Shiro 的会话管理。Shiro 的会话管理提供了更细致的控制,比如会话超时、存储和共享等功能。Authentication(认证)
认证是指验证用户身份的过程。Shiro 提供了简单的 API 来实现认证过程,比如subject.login(token)。在实际应用中,通常通过用户名和密码的组合进行认证,但 Shiro 也支持其他方式(如 OAuth2、JWT 等)。Authorization(授权)
授权是指验证用户是否具备某些权限或角色的过程。Shiro 支持基于角色和基于权限的授权,允许更精细的权限控制。通过subject.hasRole或subject.isPermitted方法,开发者可以检查用户的角色和权限。Cryptography(加密)
Shiro 内置了加密功能,提供对密码和敏感信息的加密和解密支持。它支持多种加密算法,并且在密码存储时支持散列和盐值。
Shiro 的主要功能和优势
V 哥总结几点Shiro 的主要功能和优势,这个在面试时吹牛逼用得到。
易于集成
Shiro 的 API 设计简单,易于集成到各种 Java 应用中。开发者可以基于 Shiro 提供的默认实现快速搭建一个基本的安全架构,也可以根据需要自定义各种功能。独立的会话管理
与基于 Web 容器的会话管理不同,Shiro 提供了跨环境的会话管理,可以应用于 Web 和非 Web 的环境,增加了应用的灵活性。权限控制简单而灵活
Shiro 的权限管理可以通过配置文件、注解或代码实现,提供了细粒度的访问控制。通过权限和角色的组合,开发者可以非常灵活地控制访问权限。支持多种数据源
Shiro 可以从多种数据源(如数据库、LDAP、文件等)获取用户和权限信息,方便与各种现有系统整合。支持 Web 和非 Web 环境
Shiro 不仅可以在 Web 应用中使用,也支持在桌面应用或微服务等环境中使用。
Shiro 的基本使用示例
光讲概念不是 V 哥风格,接下来,通过一个典型的 Shiro 应用来了解一下如何使用,包含配置 SecurityManager、配置 Realm、进行认证和授权等步骤。
- 配置 Shiro 环境
可以通过shiro.ini文件配置 Shiro,也可以通过代码进行配置。
[main]
# 配置 SecurityManager
securityManager = org.apache.shiro.mgt.DefaultSecurityManager
# 配置 Realm
myRealm = com.wg.MyCustomRealm
securityManager.realms = $myRealm
创建自定义 Realm
自定义 Realm 通过继承
AuthorizingRealm并实现doGetAuthenticationInfo和doGetAuthorizationInfo方法来提供用户和权限数据。
public class MyCustomRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取用户名和密码等信息,查询数据库进行认证
return new SimpleAuthenticationInfo(username, password, getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 获取用户角色和权限信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRole("admin");
info.addStringPermission("user:read");
return info;
}
}
- 使用 Shiro 进行认证和授权
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("username", "password");
try {
currentUser.login(token);
System.out.println("认证成功");
} catch (AuthenticationException ae) {
System.out.println("认证失败");
}
}
// 检查权限
if (currentUser.hasRole("admin")) {
//用输出模拟一下哈
System.out.println("用户拥有 admin 角色");
}
if (currentUser.isPermitted("user:read")) {
//用输出模拟一下哈
System.out.println("用户具有 user:read 权限");
}
通过这个简单的案例学习,咱们可以了解 Shiro 的基本使用,但这不是全部,听V哥继续慢慢道来。
场景案例
这点很重要,强调一下哈,Shiro 适合需要简洁易用、安全控制要求灵活的 Java 应用,如中小型 Web 应用、桌面应用、分布式微服务等。对于大型企业应用或需要集成多种认证方式(如 OAuth2、JWT 等)的项目,Spring Security 可能会更合适。
要在微服务架构中实现基于 Apache Shiro 的安全认证和授权,比如一个订单管理系统为例。这个系统包含两个主要服务:
- 用户服务:负责用户的注册、登录、认证等操作。
- 订单服务:允许用户创建、查看、删除订单,并限制访问权限。
咱们来看一下,这个应该怎么设计呢?
微服务案例设计
在这个场景中,我们需要以下几项核心功能:
- 用户认证:用户通过用户名和密码登录。
- 权限控制:仅管理员能删除订单,普通用户只能查看和创建订单。
- Token机制:使用 JWT Token(JSON Web Token)来管理用户的登录状态,实现无状态认证,使得在分布式环境下不依赖于单一会话。
- 跨服务认证:订单服务在接收到请求时,检查并验证用户的身份和权限。
架构和技术选型
- Spring Boot:用于快速搭建微服务。
- Shiro:实现认证、授权。
- JWT:生成和验证 Token,保持无状态认证。
- Spring Data JPA:访问数据库存储用户和订单数据。
系统结构
+------------------+ +---------------------+
| 用户服务 | | 订单服务 |
| | | |
| 用户注册、登录 | | 查看、创建、删除订单|
+------------------+ +---------------------+
| |
|----用户 Token ------|
步骤:实现微服务中的认证和授权
1. 引入必要的依赖
在 pom.xml 文件中,添加 Shiro、JWT 和 Spring Data JPA 等依赖:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
2. 配置 Shiro 与 JWT 过滤器
使用 Shiro 的自定义 JWT 过滤器实现无状态认证,通过 Token 验证用户。
public class JwtFilter extends BasicHttpAuthenticationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader("Authorization");
if (StringUtils.isBlank(token)) {
return false;
}
try {
// 解析 JWT token
JwtToken jwtToken = new JwtToken(token);
getSubject(request, response).login(jwtToken);
return true;
} catch (Exception e) {
return false;
}
}
}
3. 实现自定义 Realm
自定义 Realm,从数据库获取用户和角色信息,并使用 JWT Token 进行无状态认证。
public class JwtRealm extends AuthorizingRealm {
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String jwtToken = (String) token.getPrincipal();
// 验证 Token
String username = JwtUtil.getUsernameFromToken(jwtToken);
if (username == null) {
throw new AuthenticationException("Token 无效");
}
// 查询用户
User user = userService.findByUsername(username);
if (user == null) {
throw new AuthenticationException("用户不存在");
}
return new SimpleAuthenticationInfo(jwtToken, jwtToken, getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = JwtUtil.getUsernameFromToken(principals.toString());
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User user = userService.findByUsername(username);
// 添加角色和权限
authorizationInfo.addRole(user.getRole());
authorizationInfo.addStringPermission(user.getPermission());
return authorizationInfo;
}
}
4. 创建 JWT 工具类
编写一个工具类,用于生成和解析 JWT Token。
public class JwtUtil {
//这里替换一下你自己的secret_key
private static final String SECRET_KEY = "这里打码了";
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 hour
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public static boolean isTokenExpired(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return claims.getExpiration().before(new Date());
}
}
5. 编写用户服务和订单服务接口
用户服务接口
用户服务提供注册和登录 API。
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody User user) {
userService.save(user);
return ResponseEntity.ok("用户注册成功");
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody User user) {
User dbUser = userService.findByUsername(user.getUsername());
if (dbUser != null && dbUser.getPassword().equals(user.getPassword())) {
String token = JwtUtil.generateToken(user.getUsername());
return ResponseEntity.ok(token);
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("登录失败");
}
}
订单服务接口
订单服务在操作订单时会验证用户的角色和权限。
@RestController
@RequestMapping("/order")
public class OrderController {
@GetMapping("/{orderId}")
public ResponseEntity<?> getOrder(@PathVariable Long orderId) {
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted("order:read")) {
// 查询订单
return ResponseEntity.ok("订单详情");
}
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("无权限查看订单");
}
@DeleteMapping("/{orderId}")
public ResponseEntity<?> deleteOrder(@PathVariable Long orderId) {
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("admin")) {
// 删除订单
return ResponseEntity.ok("订单已删除");
}
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("无权限删除订单");
}
}
最后
这个案例中咱们通过如何使用 Shiro、JWT 和 Spring Boot 来构建一个无状态的微服务认证授权机制。通过 Shiro 实现用户认证和权限控制,使用 JWT 实现无状态 Token 验证。在轻量级的分布式微服务应用中,是不是使用 Shiro 感觉更加清爽呢,欢迎评论区一起讨论,关注威哥爱编程,爱上Java,一辈子。
适合才最美:Shiro安全框架使用心得的更多相关文章
- shiro安全框架
原文:http://blog.csdn.net/boonya/article/details/8233303 可能大家早先会见过 J-security,这个是 Shiro 的前身.在 2009 年 3 ...
- Shiro安全框架【快速入门】就这一篇!
Shiro 简介 照例又去官网扒了扒介绍: Apache Shiro™ is a powerful and easy-to-use Java security framework that perfo ...
- SpringBoot集成Shiro安全框架
跟着我的步骤:先运行起来再说 Spring集成Shiro的GitHub:https://github.com/yueshutong/shiro-imooc 一:导包 <!-- Shiro安全框架 ...
- Springboot整合Shiro安全框架
最近在学习Springboot,在这个过程中遇到了很多之前都没有技术知识,学习了一阵子,稍微总结一些. ---- Shiro框架 shiro框架,是一个相对比较简便的安全框架,它可以干净利落地处理身份 ...
- (转) shiro权限框架详解06-shiro与web项目整合(上)
http://blog.csdn.net/facekbook/article/details/54947730 shiro和web项目整合,实现类似真实项目的应用 本文中使用的项目架构是springM ...
- SpringBoot学习笔记(五):SpringBoot集成lombok工具、SpringBoot集成Shiro安全框架
SpringBoot集成lombok工具 什么是lombok? 自动生成setget方法,构造函数,打印日志 官网:http://projectlombok.org/features/index. 平 ...
- Shiro 安全框架详解二(概念+权限案例实现)
Shiro 安全框架详解二 总结内容 一.登录认证 二.Shiro 授权 1. 概念 2. 授权流程图 三.基于 ini 的授权认证案例实现 1. 实现原理图 2. 实现代码 2.1 添加 maven ...
- thymeleaf模板引擎shiro集成框架
shiro权限框架.前端验证jsp设计.间tag它只能用于jsp系列模板引擎. 使用最近项目thymeleaf作为前端模板引擎,采用HTML档,未出台shiro的tag lib,假设你想利用这段时间s ...
- shiro权限框架(一)
不知不觉接触shiro安全框架都快三个月了,这中间配合项目开发踩过无数的坑.现在回想总结下,也算是一种积累,一种分享.中间有不够完美的地方或者不好的地方,希望大家指出来能一起交流.在这里谢谢开涛老师的 ...
- Shiro权限框架简介
http://blog.csdn.net/xiaoxian8023/article/details/17892041 Shiro权限框架简介 2014-01-05 23:51 3111人阅读 评论 ...
随机推荐
- FFT 高精度乘法模板
#define L(x) (1 << (x)) const double PI = acos(-1.0); const int N = 1e7 + 10; double ax[N], ay ...
- 记一次解决OTA死机重启bug,如何分析与解决措施?!
背景: 平台:stm32mp151平台 什么是OTA? 说起OTA我们应该都不陌生,它是一种可以为设备无损失升级系统的方式,能将新功能远程部署到产品上. 我们不仅可以通过网络下载OTA升级包,也可以通 ...
- Spherical Voxelization
Spherical Voxelization 标签: voxelization AI 摘要: 文档介绍了球面体素化的过程,包括重要的类和方法,如ConvertToSphericalVoxel和sphe ...
- 一键k8s企业级集群部署(以k8s的1.18.0版本为例)
一.下载安装sealos wget https://github.com/fanux/sealos/releases/download/v3.2.0-beta.2/sealos && ...
- Gradio.NET 支持 .NET 8 简化 Web 应用开发
前言 Gradio.NET 是 Gradio 在 .NET 平台上的移植版本.Gradio 是一个开源的 Python 包,用于快速构建机器学习模型.API 或任意 Python 函数的演示或 Web ...
- Redis实战11-实现优惠券秒杀下单
本篇,咱们来实现优惠券秒杀下单功能.通过本篇学习,我们将会有如下收获: 1:优惠券领券业务逻辑: 2:分析在高并发情况下,出现超卖问题产生的原因: 3:解决超卖问题两种方案:版本号法及CAS法 4:乐 ...
- fluent python-chap2
1. 内置序列类型 容器序列: list tuple collections.deque 可以存放不同类型的数据. 存放的是它们所包含的任意类型的对象的引用. 扁平序列: str bytes byte ...
- vue+webpack工程中怎样在vue页面中引入第三方非标准的JS库或者方法
方法一:异步加载第三方库 在我们的vue工程中新建如下路径:src/utils/index.js,在index.js中实现如下方法: export function loadScript(url) { ...
- Unity中的光源类型(向前渲染路径进行光照计算)
Unity中的光源类型 Unity中共支持4种光源类型: 平行光 点光源 聚光灯 面光源(在光照烘焙时才可以发挥作用) 光源的属性: 位置 方向(到某个点的方向) 颜色 强度 衰减(到某个点的衰减) ...
- [TK] 盖房子 hzoi-tg#262
同机房大佬也写了这题的 题解. 通解分析 此类问题我通常喜欢归纳成一类,即阻碍联通的坐标DP. 既然是阻碍联通,那么此类问题的通用思路是这样的: 首先将dp数组图形化. 一般用一个特定图形上的特定点来 ...