概述
在开发 Web 应用程序时,安全性是一项非常重要的问题。Spring Boot Security 提供了一种简单而强大的方式来保护应用程序免受各种安全威胁。结合使用 JWT(JSON Web Token),我们可以实现一种无状态的、分布式的授权验证机制。
本文将介绍如何使用 Spring Boot Security 和 JWT 来实现授权验证模块,以增强 Web 应用程序的安全性。
步骤
1. 添加 Maven 依赖
首先,在项目的 pom.xml 文件中添加以下 Maven 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
这将添加 Spring Boot Security 和 JWT 相关的依赖项。
2. 创建用户认证服务
接下来,我们需要创建一个用户认证服务,在其中实现验证用户身份的逻辑。创建一个名为 UserService
的类,并实现 UserDetailsService
接口:
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Invalid username or password.");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(),
user.getPassword(),
Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
}
}
在上述代码中,我们使用 UserRepository
来查询数据库中的用户信息,并将其转换为实现了 UserDetails
接口的对象。这样,我们就可以在后续的认证过程中使用用户的信息。
3. 创建一个安全配置类
接下来,我们需要创建一个安全配置类,用于配置 Spring Boot Security 的行为。创建一个名为 SecurityConfig
的类,并加上 @EnableWebSecurity
注解,以启用 Spring Boot 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("/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()));
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
在上述代码中,我们通过重写 configure
方法来配置认证和授权规则。我们使用 permitAll()
允许所有用户访问 /login
路径,必须进行身份验证的其他请求将要求用户进行登录。我们还添加了两个 JWT 相关的过滤器:JwtAuthenticationFilter
和 JwtAuthorizationFilter
。
4. 创建 JWT 过滤器
接下来,我们需要创建两个 JWT 过滤器:JwtAuthenticationFilter
和 JwtAuthorizationFilter
。JwtAuthenticationFilter
用于验证用户身份,而 JwtAuthorizationFilter
用于验证用户的访问权限。
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
setFilterProcessesUrl("/login");
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
LoginUser loginUser = new ObjectMapper().readValue(req.getInputStream(), LoginUser.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException {
String token = Jwts.builder()
.setSubject(((User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SecurityConstants.SECRET.getBytes())
.compact();
res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
}
}
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(SecurityConstants.HEADER_STRING);
if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(SecurityConstants.HEADER_STRING);
if (token != null) {
String username = Jwts.parser()
.setSigningKey(SecurityConstants.SECRET.getBytes())
.parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (username != null) {
return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
}
return null;
}
return null;
}
}
在上述代码中,我们使用 JWT 来生成用户认证和授权所需的令牌。
5. 创建登录接口
最后,我们需要创建一个登录接口,以供用户进行身份验证。创建一个名为 AuthController
的类,并添加登录接口:
@RestController
public class AuthController {
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginUser loginUser) {
// Validate user credentials and authenticate
// Generate and return JWT token
}
}
在上述代码中,我们接收一个包含用户名和密码的 LoginUser
对象,并进行身份验证。验证成功后,我们生成并返回一个 JWT 令牌。
结论
通过结合使用 Spring Boot Security 和 JWT,我们可以实现一个强大、安全的授权验证模块。在本文中,我们了解了如何使用 Spring Boot Security 配置认证规则,并创建了用户认证服务、安全配置类、JWT 过滤器和登录接口。这种组合可以帮助我们有效地保护 Web 应用程序免受未经授权的访问。
希望本文能对你理解 Spring Boot Security 和 JWT 的使用有所帮助,欢迎留言进行讨论和提问。
本文来自极简博客,作者:前端开发者说,转载请注明原文链接:Spring Boot Security JWT 授权验证模块使用