下面是对于“SpringBoot集成整合JWT与Shiro流程详解”的完整攻略。
概述
在传统的Web应用中,我们通常采用用户名和密码进行身份认证,但这种方式很容易受到各种攻击,例如:暴力破解、钓鱼等。为了解决这些问题,我们可以采用JWT的方式进行身份认证,并使用Shiro进行授权管理。本文将详细介绍SpringBoot集成整合JWT与Shiro的流程。
JWT简介
JWT(JSON Web Token) 是一种基于 JSON 的开放标准(RFC 7519),用于通过网络传输包含声明的安全令牌。
JWT由三部分组成,使用‘.’进行分隔,分别为:头部(header)、载荷(payload)和签名(signature)。
JWT格式
头部(header).载荷(payload).签名(signature)
JWT特点
- 去中心化:基于 TOKEN 进行身份认证,无需访问数据库或共享会话
- 自包含:包含了所有的权利或声明,不需考虑设备或它的寄存器
- 简洁:作为构件的传输, 便于POST参数或存储于URL中
- 靠谱:数字签名保证鉴别使用者的可信性和不可否认性
Shiro简介
Shiro是一个Java安全框架,提供身份认证、授权、加密、会话管理等功能。Shiro的优点是简单易用,且不依赖Spring框架,可以与Spring等框架很好地进行集成。
整合步骤
下面是我们整合的步骤:
- 引入依赖
- 编写Shiro认证过滤器
- 编写JWT工具类
- 编写登录接口
- 编写需要鉴权的接口
1. 引入依赖
在pom.xml中引入以下依赖:
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- Shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
2. 编写Shiro认证过滤器
创建Shiro认证过滤器,用于鉴权。这里我们先创建Controller和Service层,后面会用到。
Controller
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
public ResultBean login(User user) {
// 登录操作,省略...
}
@RequiresRoles("admin")
@GetMapping("/admin")
public ResultBean admin() {
// 管理员操作,省略...
}
}
Service
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findByUsername(String username) {
// 查询操作,省略...
}
@Override
public String generateToken(User user) {
// JWT生成Token操作,省略...
}
}
在Shiro中,每个请求都需要经过一个认证过滤器,我们可以通过继承AuthenticatingFilter
实现自己的认证过滤器。
JWTFilter
public class JWTFilter extends AuthenticatingFilter {
@Autowired
private UserService userService;
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String token = getToken(request);
if (StringUtils.isBlank(token)) {
return false;
}
try {
JWTUtil.verify(token);
String username = JWTUtil.getUsername(token);
User user = userService.findByUsername(username);
if (user == null) {
return false;
}
// 设置当前请求用户信息
SecurityUtils.getSubject().login(new UsernamePasswordToken(username, token));
return true;
} catch (Exception e) {
return false;
}
}
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
return false;
}
private String getToken(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (StringUtils.isBlank(token)) {
token = request.getParameter("token");
}
return StringUtils.removeStart(token, "Bearer ");
}
}
3. 编写JWT工具类
在项目中,我们需要编写JWT生成和鉴权的工具类。
JWTUtil
public class JWTUtil {
public static final String CLAIM_KEY_USERNAME = "sub";
public static final String CLAIM_KEY_CREATED = "created";
private static final String SECRET = "123456";
private static final long EXPIRATION_TIME = 3600 * 24 * 7;
/**
* 生成JWT Token
*/
public static String generateToken(User user) {
Map<String, Object> claims = new HashMap<>(2);
claims.put(Claims.SUBJECT, user.getUsername());
claims.put(Claims.ISSUED_AT, new Date());
return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, SECRET).compact();
}
/**
* 从Token中获取用户名
*/
public static String getUsername(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
/**
* 校验Token是否合法
*/
public static boolean verify(String token) {
try {
Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
return true;
} catch (Exception e) {
return false;
}
}
private static Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + EXPIRATION_TIME * 1000);
}
private static <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private static Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
}
}
4. 编写登录接口
在用户进行登录时,我们需要获取到前端传入的用户名和密码,进行验证操作。如果验证成功,我们就需要生成加密后的JWT Token,并将其返回给前端。
UserController
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
public ResultBean login(User user) {
// 1. 进行 Shiro 登录操作
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(token);
} catch (AuthenticationException e) {
return ResultBean.error("账号密码错误!");
}
// 2. 生成 JWT Token
user = userService.findByUsername(user.getUsername());
String jwtToken = userService.generateToken(user);
// 3. 将 Token 返回给前端
return ResultBean.success(jwtToken);
}
}
5. 编写需要鉴权的接口
最后,我们需要编写需要被鉴权的接口,例如管理员操作接口。
UserController
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequiresRoles("admin")
@GetMapping("/admin")
public ResultBean admin() {
// 管理员操作,省略...
}
}
示例
为了更好地理解我们上面的步骤,这里提供两个实际案例供参考。
示例一:登录接口
在项目中,我们有一个登录接口 /login
,当用户输入正确的用户名和密码时,我们将会返回一个 JWT Token。
在 UserController
中,我们需要编写如下登录函数逻辑:
@PostMapping("/login")
public ResultBean login(User user) {
// 1. 进行 Shiro 登录操作
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(token);
} catch (AuthenticationException e) {
return ResultBean.error("账号密码错误!");
}
// 2. 生成 JWT Token
user = userService.findByUsername(user.getUsername());
String jwtToken = userService.generateToken(user);
// 3. 将 Token 返回给前端
return ResultBean.success(jwtToken);
}
示例二:管理员操作接口
在项目中,我们有一个管理员操作接口 /admin
,只有具有 admin
角色的用户才能访问该接口。
在 UserController
中,我们需要在接口上标注必须具有 admin
角色的注解,即 @RequiresRoles("admin")
:
@RequiresRoles("admin")
@GetMapping("/admin")
public ResultBean admin() {
// 进行管理员操作逻辑
return ResultBean.success();
}
总结
通过本文的分享,相信大家学习了如何使用SpringBoot集成整合JWT与Shiro的流程。目前JWT和Shiro都是比较热门的安全框架,使用起来比较简便,也可以非常好的进行集成,方便我们进行项目开发。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot集成整合JWT与Shiro流程详解 - Python技术站