下面是详细的讲解和示例:
一、SpringBoot集成SpringSecurity和JWT的基础配置
Spring Security 是一款强大、灵活并且广泛使用的安全框架,它基于 Spring 构建,提供了一种基于角色的访问控制、认证和授权等安全解决方案。而 JWT 是一种轻量级的认证机制,它可以在用户和服务器之间进行授权传递,用于跨域认证。在本文中,我们将讨论如何将 Spring Boot、Spring Security 和 JWT 整合在一起,从而实现基于 JWT 的认证和授权机制。
首先,我们需要添加相关依赖,具体内容如下:
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.1</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.1</version>
<scope>runtime</scope>
</dependency>
其中, spring-boot-starter-web
是 Spring Boot 的 Web 依赖, spring-boot-starter-security
是 Spring Security 的依赖, jjwt-api
是 JWT API 的依赖, jjwt-impl
是 JWT 实现的依赖, jjwt-jackson
是用于 JWT 数据反序列化的依赖。
接下来,我们需要进行一些基础的配置,具体内容如下:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Bean
public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationFilter();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated();
// 添加JWT filter
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
}
在上述配置中,我们首先注入了两个对象,分别是 UserDetailsService
和 JwtAuthenticationEntryPoint
。UserDetailsService
是一个用于获取用户相关信息的接口,我们需要自定义它来获取用户信息。JwtAuthenticationEntryPoint
则是在用户认证失败时被调用,返回未认证的错误信息。
接着,我们创建了一个 JwtAuthenticationFilter
的实例,并将它注入到 Bean 中。JwtAuthenticationFilter
是我们自定义的 JWT 过滤器,该过滤器用于解析 JWT 并验证用户信息。当请求带有 JWT 的 Authorization 头信息时将自动进入该过滤器的处理流程。
接下来,我们通过重写 configure(AuthenticationManagerBuilder auth)
、 configure(HttpSecurity http)
方法来对 Spring Security 进行配置。其中, configure(AuthenticationManagerBuilder auth)
方法用于配置用户信息服务和密码加密方式, configure(HttpSecurity http)
方法用于配置 Web 安全性。
在 configure(HttpSecurity http)
方法中,我们首先禁用了 Spring Security 对 CSRF(跨站请求伪造)的防护。接着,我们将会话管理策略设置为无状态,这意味着用户不需要记录会话的状态信息。在本文的示例中,我们将使用 JWT Token 来代替会话。
最后,我们通过 http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
来为所有请求添加了一个自定义的 JWT 过滤器,用于解析 JWT 并验证用户信息。
上述配置是 Spring Security 整合 JWT 的基础配置,接下来我们需要编写具体的实现类。
二、SpringBoot集成SpringSecurity和JWT实现用户认证和授权
1. 用户认证接口实现
我们需要实现一个用户认证接口,用于处理用户的登录请求。具体代码如下:
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginForm loginForm) {
// 用户认证
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginForm.getUsername(),
loginForm.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成JWT Token
UserDetails userDetails = userDetailsService.loadUserByUsername(loginForm.getUsername());
String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
}
在上述代码中,我们首先注入了三个对象,分别是 AuthenticationManager
、 JwtTokenUtil
和 UserDetailsService
。然后,我们编写了一个 login()
方法来处理用户认证请求。
该方法首先使用 AuthenticationManager
对象进行用户认证,如果用户名和密码均正确,则返回一个 Authentication
对象。接着,我们将认证状态存储在 SecurityContextHolder
中。
最后,我们通过 userDetailsService.loadUserByUsername(loginForm.getUsername())
方法获取用户信息,并使用 jwtTokenUtil.generateToken(userDetails)
方法生成 JWT Token。最后通过 ResponseEntity 将 Token 返回给客户端。
2. JWT 过滤器实现
我们也需要实现一个 JWT 过滤器,用于拦截所有请求并进行 JWT 解析和用户认证。具体代码如下:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (header == null || !header.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
final String token = header.substring(7);
String username = jwtTokenUtil.getUsernameFromToken(token);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
在上述代码中,我们首先注入了两个对象,分别是 JwtTokenUtil
和 UserDetailsService
。具体实现可以看到,我们从 HTTP 请求头信息中获取 JWT Token,然后使用该 Token 获取到用户信息并进行用户认证。
如果 Token 无效,则认证失败;否则,我们创建一个 Authentication
对象并将其设置到 SecurityContextHolder
中。
三、示例
下面是一些用于测试的示例代码。
1. 用户类
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private boolean isEnabled;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean isEnabled() {
return isEnabled;
}
public void setEnabled(boolean enabled) {
isEnabled = enabled;
}
// other UserDetails methods
}
2. 用户服务类实现
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = new User();
user.setId(1);
user.setUsername(username);
user.setPassword(new BCryptPasswordEncoder().encode("password"));
user.setEnabled(true);
return user;
}
}
3. JWT Token 工具类实现
@Component
public class JwtTokenUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "iat";
private static final String SECRET = "jwtsecret";
private static final long EXPIRATION_TIME = 864_000_000; // 10天,以毫秒为单位
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
public String getUsernameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + EXPIRATION_TIME);
}
private boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
private Date getExpirationDateFromToken(String token) {
Date expiration;
try {
final Claims claims = getClaimsFromToken(token);
expiration = claims.getExpiration();
} catch (Exception e) {
expiration = null;
}
return expiration;
}
}
4. 登录表单实现
public class LoginForm {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
以上就是 Spring Boot 集成 Spring Security 和 JWT 的实现过程和示例代码。希望此篇文章对你有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot集成SpringSecurity和JWT做登陆鉴权的实现 - Python技术站