赞
踩
JWT(JSON Web Token)是一种基于JSON格式的轻量级的、用于身份认证的开放标准。它通过在用户和服务器之间传递一个安全的、可靠的、独立的JSON对象来进行身份验证和授权。它如今被广泛应用在RESTful API中,因为RESTful API通常不会维持任何状态信息。
JWT是一种轻量级、可扩展、可自包含的身份验证和授权机制。它是由三个部分组成:头部(Header)、载荷(Payload)和签名(Signature)。它的目的是为了在网络应用间传递声明信息,以便在某些情况下对用户进行身份验证和授权。
JWT有以下几个特点:
JWT由三个部分组成:头部、载荷和签名。
JWT头部是一个JSON对象,它描述了生成和处理该JWT所需要的算法和类型信息。例如:
{
"alg": "HS256",
"typ": "JWT"
}
其中,"alg"表示签名算法,"typ"表示令牌类型。
JWT载荷是一个JSON对象,它包含了有关令牌和声明的信息。例如:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
其中,"sub"表示主题(Subject),"name"表示名称,"iat"表示令牌的发行时间(Issued At)。
除了这些声明外,JWT还支持自定义声明。例如:
{
"iss": "http://example.com",
"exp": 1516239922,
"custom_key": "custom_value"
}
其中,"iss"表示颁发者(Issuer),"exp"表示到期时间(Expiration Time),"custom_key"是自定义声明。
JWT签名用于验证消息的发送方和消息的完整性。签名由头部、载荷和密钥组成,并使用指定的算法进行计算。例如,使用HS256算法可以使用以下方法生成签名:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
其中,"."是分隔符,"secret"是密钥。
JWT的工作流程可以描述为以下三个步骤:
具体的工作过程如下:
JWT主要应用于Web应用程序和RESTful API,以下是它的一些常见应用场景:
JWT的优势包括:
使用JWT时,需要注意以下几点来避免安全风险:
JJWT 是一个流行的 Java JWT 库,它提供了一种创建、编码和解码 JWT 的简便方法。以下是一个基于 JJWT 的示例:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; public class JwtUtils { private static final long EXPIRATION_TIME = 864_000_000; // 10 days private static final String SECRET_KEY = "secretKey"; public static String generateToken(String username) { Date expiryDate = new Date(System.currentTimeMillis() + EXPIRATION_TIME); return Jwts.builder() .setSubject(username) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, 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 isTokenValid(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; } catch (Exception e) { return false; } } }
在这个示例中,JwtUtils
类包含了生成令牌、从令牌中获取用户名和验证令牌的三个方法。注意,在实际使用中,SECRET_KEY
应该更加复杂。
Spring Security 是一个流行的安全框架,它提供了一种轻松的方式来保护应用程序和 APIs。Spring Security 支持使用 JWT 进行身份验证和授权。以下是一个基于 Spring Security 的示例:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
在这个示例中,SecurityConfig
类扩展了 WebSecurityConfigurerAdapter
类,并覆盖了其中的 configure(HttpSecurity http)
方法。在这个方法中,Spring Security 配置了哪些 URL 公开,哪些需要进行身份验证,并且禁用了 CSRF 保护。配置完后,Spring Security 将 JWT 过滤器添加到 UsernamePasswordAuthenticationFilter
前面。
在实现身份验证之前,需要创建一个用户服务类并实现 UserDetailsService
接口。以下是一个示例:
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return new org.springframework.security.core.userdetails.User(user.getUsername(),
user.getPassword(), new ArrayList<>());
}
}
在这个示例中,UserService
类实现了 UserDetailsService
接口,并覆盖其中的 loadUserByUsername(String username)
方法。该方法根据给定的用户名查找用户,如果用户不存在,则抛出 UsernameNotFoundException
异常。
最后,需要实现一个 JWT 过滤器来验证 JWT 并将身份验证信息加载到 Spring Security 的上下文中。以下是一个基于 Spring Security 的 JWT 过滤器示例:
public class JwtAuthenticationFilter extends OncePerRequestFilter { private static final String AUTHORIZATION_HEADER = "Authorization"; private static final String TOKEN_PREFIX = "Bearer "; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String token = parseToken(request); if (token != null && JwtUtils.isTokenValid(token)) { String username = JwtUtils.getUsernameFromToken(token); UserDetails userDetails = userService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } } catch (Exception e) { logger.error("Error while processing authentication request", e); } filterChain.doFilter(request, response); } private String parseToken(HttpServletRequest request) { String header = request.getHeader(AUTHORIZATION_HEADER); if (header != null && header.startsWith(TOKEN_PREFIX)) { return header.substring(TOKEN_PREFIX.length()); } return null; } }
在这个示例中,JWT 过滤器实现了 OncePerRequestFilter
类,并覆盖了其中的 doFilterInternal
方法。在该方法中,它从请求头中获取 JWT,验证 JWT,将身份验证信息加载到 Spring Security 的上下文中,然后放行请求。
Apache Shiro 是一个功能丰富的安全框架,可以用于保护 Java 应用程序和 APIs。Shiro 支持使用 JWT 进行身份验证和授权。以下是一个基于 Shiro 的示例:
@Configuration public class ShiroConfig { @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm()); return securityManager; } @Bean public JwtRealm realm() { JwtRealm realm = new JwtRealm(); realm.setCredentialsMatcher(jwtCredentialsMatcher()); return realm; } @Bean public JwtCredentialsMatcher jwtCredentialsMatcher() { return new JwtCredentialsMatcher(); } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean(); filterFactoryBean.setSecurityManager(securityManager); filterFactoryBean.setFilters(filters()); filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinition()); return filterFactoryBean; } private Map<String, String> filterChainDefinition() { Map<String, String> filterChainDefinition = new LinkedHashMap<>(); filterChainDefinition.put("/api/auth/**", "anon"); filterChainDefinition.put("/**", "jwt"); return filterChainDefinition; } private Map<String, Filter> filters() { Map<String, Filter> filters = new HashMap<>(); filters.put("jwt", new JwtFilter()); return filters; } }
在这个示例中,Shiro 配置了一个名为 securityManager
的安全管理器,其中包含一个名为 realm
的域。realm
类需要实现 org.apache.shiro.realm.Realm
接口,并覆盖其中的 supports(AuthenticationToken token)
和 getAuthenticationInfo(AuthenticationToken token)
方法来验证 JWT。
类似于 Spring Security,需要实现一个 JWT 过滤器来验证 JWT 并将身份验证信息加载到 Shiro 的上下文中。以下是一个基于 Shiro 的 JWT 过滤器示例:
public class JwtFilter extends AuthenticatingFilter { @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) { String token = getToken(request); if (StringUtils.isNotBlank(token) && JwtUtils.isTokenValid(token)) { return new JwtToken(token); } return null; } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { HttpServletResponse httpServletResponse = (HttpServletResponse) response; httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } private String getToken(ServletRequest request) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; String token = httpServletRequest.getHeader("Authorization"); if (StringUtils.isNotBlank(token) && token.startsWith("Bearer ")) { return token.substring(7); } else { return null; } } }
在这个示例中,JWT 过滤器扩展了 AuthenticatingFilter
类,并覆盖了其中的 createToken(ServletRequest request, ServletResponse response)
和 onAccessDenied(ServletRequest request, ServletResponse response)
方法。createToken
方法创建一个 JwtToken 对象,并将 JWT 设置为凭据。onAccessDenied
方法在未找到 JWT 时返回 UNAUTHORIZED 状态码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。