下面我会详细讲解“spring security结合jwt实现用户重复登录处理”的完整攻略。
概述
在使用JWT(Json Web Token)作为身份认证的情况下,用户可以随时提供令牌来访问应用程序,这使得应用程序无法管理用户的会话状态,例如强制注销用户或在重复登录情况下限制访问。为了解决这个问题,我们可以使用Spring Security来管理用户登录状态。本文将描述如何使用Spring Security和JWT结合实现用户重复登录的控制。
步骤
以下是实现用户重复登录处理的步骤:
步骤1: 添加依赖关系
首先,我们需要添加下面两个依赖关系:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
步骤2: 创建JWT工具类
我们需要一个JWT工具类来创建和解析JWT令牌。以下是示例代码:
@Component
public class JwtTokenUtil {
private final String secret = "mySecret";
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder().setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
.signWith(SignatureAlgorithm.HS512, secret).compact();
}
public boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
private boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public Date getExpirationDateFromToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getExpiration();
}
public String getUsernameFromToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
}
}
步骤3: 创建一个JWT过滤器
我们需要一个JWT过滤器来解析JWT令牌并将其绑定到Spring Security上下文中。以下是示例代码:
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsServiceImpl userService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authToken = request.getHeader("Authorization");
String username = jwtTokenUtil.getUsernameFromToken(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}
步骤4: 创建一个JWT登录策略
我们需要一个JWT登录策略来定义如何响应登录和注销请求。以下是示例代码:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new JwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().logout().logoutSuccessHandler((request, response, authentication) -> response.setStatus(HttpServletResponse.SC_OK))
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true).permitAll()
.and().exceptionHandling().authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"))
.and().sessionManagement().maximumSessions(1).expiredUrl("/sessionExpired");
}
@Bean
public JwtTokenUtil jwtTokenUtil() {
return new JwtTokenUtil();
}
}
步骤5: 创建用户服务
我们需要一个用户服务来加载用户详细信息。以下是示例代码:
@Service
public class UserDetailsServiceImpl 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("User not found with username: " + username);
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());
}
}
步骤6: 创建示例Controller
我们需要一个示例Controller来测试用户重复登录控制的效果。以下是示例代码:
@RestController
public class AuthController {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody User user) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
final UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new AuthToken(token));
}
@PostMapping("/logout")
public ResponseEntity<?> logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
SecurityContextHolder.clearContext();
if (session != null) {
session.invalidate();
}
return ResponseEntity.ok().body("Logged out successfully");
}
}
class AuthToken {
private String token;
public AuthToken(String token) {
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
以上就是使用Spring Security和JWT结合实现用户重复登录处理的完整攻略。使用上述步骤,我们可以实现对用户登录状态的完整管理,实现更加安全的用户认证和会话管理。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:spring security结合jwt实现用户重复登录处理 - Python技术站