package com.picacho.util;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.picacho.common.Consts;
import com.picacho.common.Status;
import com.picacho.config.JwtConfig;
import com.picacho.exception.SecurityException;
import com.picacho.vo.UserPrincipal;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@EnableConfigurationProperties(JwtConfig.class)
@Configuration
@Slf4j
public class JwtUtil {
@Autowired
private JwtConfig jwtConfig;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 创建JWT
*
* @param rememberMe 记住我
* @param id 用户id
* @param subject 用户名
* @param roles 用户角色
* @param authorities 用户权限
* @return JWT
*/
public String createJWT(Boolean rememberMe, Long id, String subject, List<String> roles, Collection<? extends GrantedAuthority> authorities) {
Date now = new Date();
JwtBuilder builder = Jwts.builder()
.setId(id.toString())
.setSubject(subject)
.setIssuedAt(now)
.signWith(SignatureAlgorithm.HS256, jwtConfig.getKey())
.claim("roles", roles)
.claim("authorities", authorities);
// 设置过期时间
Long ttl = rememberMe ? jwtConfig.getRemember() : jwtConfig.getTtl();
if (ttl > 0) {
builder.setExpiration(DateUtil.offsetMillisecond(now, ttl.intValue()));
}
String jwt = builder.compact();
// 将生成的JWT保存至Redis
stringRedisTemplate.opsForValue()
.set(Consts.REDIS_JWT_KEY_PREFIX + subject, jwt, ttl, TimeUnit.MILLISECONDS);
return jwt;
}
/**
* 创建JWT
*
* @param authentication 用户认证信息
* @param rememberMe 记住我
* @return JWT
*/
public String createJWT(Authentication authentication, Boolean rememberMe) {
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
return createJWT(rememberMe, userPrincipal.getId(), userPrincipal.getUsername(), userPrincipal.getRoles(), userPrincipal.getAuthorities());
}
/**
* 解析JWT
*
* @param jwt JWT
* @return {@link Claims}
*/
public Claims parseJWT(String jwt) {
try {
Claims claims = Jwts.parser()
.setSigningKey(jwtConfig.getKey())
.parseClaimsJws(jwt)
.getBody();
String username = claims.getSubject();
String redisKey = Consts.REDIS_JWT_KEY_PREFIX + username;
// 校验redis中的JWT是否存在
Long expire = stringRedisTemplate.getExpire(redisKey, TimeUnit.MILLISECONDS);
if (Objects.isNull(expire) || expire <= 0) {
throw new SecurityException(Status.TOKEN_EXPIRED);
}
// 校验redis中的JWT是否与当前的一致,不一致则代表用户已注销/用户在不同设备登录,均代表JWT已过期
String redisToken = stringRedisTemplate.opsForValue()
.get(redisKey);
if (!StrUtil.equals(jwt, redisToken)) {
throw new SecurityException(Status.TOKEN_OUT_OF_CTRL);
}
return claims;
} catch (ExpiredJwtException e) {
log.error("Token 已过期");
throw new SecurityException(Status.TOKEN_EXPIRED);
} catch (UnsupportedJwtException e) {
log.error("不支持的 Token");
throw new SecurityException(Status.TOKEN_PARSE_ERROR);
} catch (MalformedJwtException e) {
log.error("Token 无效");
throw new SecurityException(Status.TOKEN_PARSE_ERROR);
} catch (SignatureException e) {
log.error("无效的 Token 签名");
throw new SecurityException(Status.TOKEN_PARSE_ERROR);
} catch (IllegalArgumentException e) {
log.error("Token 参数不存在");
throw new SecurityException(Status.TOKEN_PARSE_ERROR);
}
}
/**
* 设置JWT过期
*
* @param request 请求
*/
public void invalidateJWT(HttpServletRequest request) {
String jwt = getJwtFromRequest(request);
String username = getUsernameFromJWT(jwt);
// 从redis中清除JWT
stringRedisTemplate.delete(Consts.REDIS_JWT_KEY_PREFIX + username);
}
/**
* 根据 jwt 获取用户名
*
* @param jwt JWT
* @return 用户名
*/
public String getUsernameFromJWT(String jwt) {
Claims claims = parseJWT(jwt);
return claims.getSubject();
}
/**
* 从 request 的 header 中获取 JWT
*
* @param request 请求
* @return JWT
*/
public String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("picacho ")) {
return bearerToken.substring(8);
}
return null;
}
}
spring-security-project.zip- Spring Security实现RBAC权限模型demo
需积分: 0 189 浏览量
更新于2023-02-09
1
收藏 195KB ZIP 举报
Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架,广泛用于Java应用程序的安全管理。在本示例中,"spring-security-project.zip" 提供了一个基于RBAC(Role-Based Access Control)权限模型的演示项目。RBAC是一种常用的权限管理机制,它通过角色将权限与用户关联,使得权限管理更加灵活和安全。
我们需要了解Spring Security的基本概念。它主要包括以下几个核心组件:
1. **过滤器链**:Spring Security通过一系列过滤器来拦截HTTP请求,执行身份验证和授权过程。
2. **AuthenticationManager**:负责处理认证请求,验证用户提供的凭证,如用户名和密码。
3. **UserDetailsService**:提供用户信息,通常是从数据库或其他数据源获取。
4. **AccessDecisionManager**:决定是否允许访问受保护的资源,基于用户的权限和角色。
5. **Authorization**:授权机制,包括基于角色的授权(RBAC)和其他策略。
在RBAC模型中,我们有三个主要的概念:
- **Role(角色)**:代表了一组权限,比如管理员、普通用户等。
- **Permission(权限)**:具体的操作,比如查看、编辑、删除等。
- **User(用户)**:可以被分配一个或多个角色。
在Spring Security中实现RBAC,你需要以下步骤:
1. **配置SecurityContextHolder**:这是Spring Security的核心,用于存储当前的SecurityContext,其中包含Authentication对象。
2. **定义Role和Permission**:在数据库中创建角色和权限表,并为每个角色分配相应的权限。
3. **实现UserDetailsService**:根据用户名查询对应的UserDetails,UserDetails包含了用户的账号信息、角色等。
4. **配置AuthenticationManager**:通过UserDetailsService加载用户信息,并进行认证。
5. **配置FilterSecurityInterceptor**:设置访问决策策略,通常基于URL路径,关联到特定的角色。
6. **定义访问控制规则**:使用`@Secured`或`@PreAuthorize`注解,或者在Web安全配置中定义访问控制规则。
7. **权限表达式**:使用Spring Expression Language (SpEL) 来表达更复杂的授权逻辑,如`hasRole()`和`hasPermission()`。
8. **自定义权限增强**:如果需要更细粒度的控制,可以实现自定义的AccessDecisionVoter。
在压缩包"spring-security-project"中,你可能找到以下关键文件和目录:
- `src/main/java`: 存放Java源代码,包括配置类和业务逻辑。
- `src/main/resources`: 存放配置文件,如Spring Security的XML配置或Java配置。
- `pom.xml`: Maven项目的配置文件,包含了依赖管理。
- `webapp/WEB-INF`: 可能包含Spring Security的web配置,如web.xml。
这个示例项目应该会包含一个示例的用户界面,展示如何登录、如何根据角色访问受限页面,以及如何进行权限控制。通过分析源代码,你可以学习如何将这些概念应用到实际项目中。
总结来说,"spring-security-project.zip"提供了一个使用Spring Security实现RBAC权限模型的实例,涵盖了认证、授权、角色和权限的管理,是学习和理解Spring Security安全框架的好材料。