下面我将为大家详细讲解 "解析SpringSecurity+JWT认证流程实现" 的完整攻略。
1. JWT简介
JSON Web Token(JWT)是一种定义了一种紧凑且自包含的方式,可以用于将各种信息传递给另一个系统。JWT 在 Web 应用中得到广泛的应用,其最大的优势就是可以在客户端和服务器之间,通过方式方便快捷的的方式实现身份认证和授权。
JWT由三部分组成:头部(header)、载荷(payload)、签名(signature)。
- 头部(header):由两部分组成,一是声明 JWT 的类型,二是声明所使用的算法。
-
载荷(payload):实际存放有用信息的地方,JWT 标准定义了7个官方字段,同时你也可以定义自己的字段,常用的有4个:
-
iss(issuer):签发JWT的人
- sub(subject):设置JWT面向的对象,一般是用户
- exp(expiration):过期时间
-
iat(issued at):签发时间
-
签名(signature):由头部、载荷及秘钥生成的签名,用于认证数据的确来自于发送方且未被篡改。
2. Spring Security 简介
Spring Security 是 Spring 框架中的一个安全框架,集成了许多企业级的安全控制功能。Spring Security 基本实现了 Web 应用安全控制的三大主要功能:认证、授权和攻击防护。
3. Spring Security + JWT 实现认证流程
Spring Security 和 JWT 相结合的认证流程大概分为以下步骤:
- 用户向服务器发送用户名和密码(或其他身份验证信息)
- 服务器收到请求后,验证用户名和密码
- 验证通过后,服务器生成 JWT
- 服务器将 JWT 返回给用户
- 用户每次向服务器发送请求时,需要在 HTTP 头中带上 JWT
- 服务器通过解码 JWT 来验证用户的身份
下面,我们结合 Spring Security + JWT 实现认证流程的完整攻略。
3.1 添加依赖库
在 pom.xml 文件中添加如下依赖库:
<!-- Spring Security 依赖库 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT 依赖库 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
3.2 配置 Spring Security
在 Spring Security 中,我们需要配置身份验证 (Authentication) 和授权 (Authorization) 的逻辑。
3.2.1 配置身份验证逻辑
通常,Spring Security 的身份验证逻辑都会写在自定义的 SecurityConfig 类中。下面我们先定义 JwtUser 和 JwtUserDetails 两个类,表示登录用户和用户细节信息:
@Data
public class JwtUser implements UserDetails {
private Long id;
private String username;
private String password;
private List<GrantedAuthority> authorities;
// ... getter, setter, overrides
}
public class JwtUserDetails {
private Long id;
private String username;
private String password;
private List<String> roles;
public JwtUserDetails(Long id, String username, String password, List<String> roles) {
this.id = id;
this.username = username;
this.password = password;
this.roles = roles;
}
public List<GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
其中,JwtUser 是 UserDetails 接口的实现类,JwtUserDetails 则是 JwtUser 的详细信息,它里面包含了用户的角色(Role)等信息。
接下来,我们需要自定义一个 AuthenticationProvider 实现类,用于验证用户身份和密码:
@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Autowired
private JwtTokenUtils jwtTokenUtils;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
JwtUserDetails userDetails = (JwtUserDetails) jwtUserDetailsService.loadUserByUsername(username);
if (!password.equals(userDetails.getPassword())) {
// 密码错误
throw new BadCredentialsException("Invalid username or password");
}
JwtUser jwtUser = new JwtUser(userDetails.getId(), userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
// 生成 JWT
String jwtToken = jwtTokenUtils.generateToken(jwtUser);
return new UsernamePasswordAuthenticationToken(username, jwtToken, userDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
在身份验证逻辑中,我们需要注入 JwtUserDetailsService 和 JwtTokenUtils,其中 JwtTokenUtils 用于生成 JWT。
3.2.2 配置授权逻辑
在 Spring Security 中,我们可以通过配置 HttpSecurity 实现请求授权。以下是示例代码:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthorizationFilter jwtAuthorizationFilter;
// 自定义身份验证逻辑
@Autowired
private JwtAuthenticationProvider jwtAuthenticationProvider;
// 忽略验证的路径
private static final String[] AUTH_WHITELIST = {
"/login",
"/logout"
};
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.cors()
.and()
.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.anyRequest().authenticated()
.and()
// 使用自定义身份验证逻辑
.authenticationProvider(jwtAuthenticationProvider)
// 添加自定义验证过滤器,用于从请求头中获取JWT并进行身份验证
.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class)
.headers()
.cacheControl();
}
}
在授权逻辑中,我们指定了白名单路径和自定义身份验证逻辑。其中,JwtAuthorizationFilter 是一个自定义的 Filter,它用于从请求头中获取 JWT 并进行身份验证。
3.3 配置 JWT
在 Spring Security 中,我们通过自定义拦截器 Filter 来获取并解析 JWT。下面是示例代码:
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtils jwtTokenUtils;
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String jwtToken = request.getHeader("Authorization");
if (!StringUtils.isEmpty(jwtToken)) {
String username = jwtTokenUtils.getUsernameFromToken(jwtToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtils.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
}
}
在 JwtAuthorizationFilter 中,我们通过配置和注入 JwtTokenUtils 和 JwtUserDetailsService,并从请求头中获取 JWT,然后解析 JWT 并验证用户身份。
3.4 示例
下面我们模拟一个用户登录和获取数据的过程。
我们可以在 UserController 中添加以下两个方法:
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody Map<String, String> user) {
String username = user.get("username");
String password = user.get("password");
// 构造一个 Authentication 对象,用于 Spring Security 做身份验证
Authentication authentication = new UsernamePasswordAuthenticationToken(username, password);
// 调用 AuthenticationManager 的 authenticate 方法进行身份验证
Authentication result = authenticationManager.authenticate(authentication);
// 登录成功后,将 JWT 和用户信息一同返回给前端
JwtUser jwtUser = (JwtUser) result.getPrincipal();
Map<String, String> response = new HashMap<>();
response.put("username", jwtUser.getUsername());
response.put("token", (String) result.getCredentials());
return ResponseEntity.ok(response);
}
@GetMapping("/data")
public List<String> getData() {
return Arrays.asList("data1", "data2", "data3", "data4");
}
}
在为 /user/login 这个接口配置 JwtAuthenticationProvider 之后,可以直接调用这个接口完成身份认证,并获取 JWT。
curl -X POST -H "Content-Type: application/json" \
-d '{"username": "alice", "password": "123456"}' \
http://localhost:8080/user/login
假设身份认证成功后,返回的 JSON 中包含了 JWT,接着我们可以使用这个 JWT 向 /user/data 接口请求数据:
curl -H "Authorization: [JWT]" \
http://localhost:8080/user/data
3.5 注意事项
以上是结合 Spring Security 和 JWT 实现了登录认证的流程,但需要注意以下问题:
- JWT 需要使用 HTTPS 安全通道传输,防止 JWT 被窃取。
- JWT 的数据量不宜过大,否则将影响传输效率,可以使用对称加密或非对称加密来减少签名长度,但需要权衡安全和效率。
- JWT 需要设置过期时间,通常是在生成 JWT 的时候设置一个时间戳,超过时间戳则 JWT 失效。
- JWT 需要妥善保管,防止 JWT 被窃取。
- 在验证 JWT 的签名时,需要使用同一的密钥(key),否则会验证不过。
4. 总结
以上就是解析 Spring Security + JWT 认证流程实现的完整攻略,我们可以通过简单的配置和代码实现一个标准的身份认证和授权逻辑,同时使用 JWT 来保护用户身份和数据。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:解析SpringSecurity+JWT认证流程实现 - Python技术站