admin管理员组

文章数量:1125455

I am developing a Spring Boot application with spring-boot-starter-security and Thymeleaf. I need to implement JWT-based authentication and ensure the following:

  1. The JWT should be stored in an HTTP-only cookie.

  2. The session and cookies should be cleared upon logout.

  3. A user should not have duplicate sessions (only one active session should be allowed). If the same user logs in from another browser or device, the previous session should be invalidated.

I am currently not using any external JWT libraries and am relying on Spring Security and custom code for JWT generation/validation. I have configured the logout functionality, but I need help with:

  1. Preventing duplicate sessions.

  2. Clearing JWT cookies and invalidating sessions on logout.

Security Configuration:

@Configuration

public class SecurityConfig {

@Bean

public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

    http

        .csrf().disable()

        .authorizeRequests()

        .antMatchers("/login", "/register").permitAll()

        .anyRequest().authenticated()

        .and()

        .formLogin().loginPage("/login").permitAll()

        .and()

        .logout().logoutUrl("/logout").logoutSuccessUrl("/login")

            .addLogoutHandler((request, response, authentication) -> {

                // Clear the JWT cookie

                Cookie cookie = new Cookie("JWT", null);

                cookie.setHttpOnly(true);

                cookie.setPath("/");

                cookie.setMaxAge(0); // Delete the cookie

                response.addCookie(cookie);

            })

            .invalidateHttpSession(true)

            .clearAuthentication(true)

        .and()

        .sessionManagement()

            .maximumSessions(1) // Allow only one session per user

            .expiredUrl("/login?expired=true") // Redirect if session is invalidated

            .and()

            .sessionFixation().newSession();



    return http.build();

}

}

JWT Utility Class:

import javax.crypto.Mac;

import javax.crypto.spec.SecretKeySpec;

import java.util.Base64;

public class JwtUtil {

private static final String SECRET_KEY = "your-256-bit-secret";

private static final String ALGORITHM = "HmacSHA256";



public String generateToken(String username) {

    long now = System.currentTimeMillis();

    long expiry = now + 3600000; // 1 hour



    String header = Base64.getEncoder().encodeToString("{\"alg\":\"HS256\",\"typ\":\"JWT\"}".getBytes());

    String payload = Base64.getEncoder().encodeToString(

        ("{\"sub\":\"" + username + "\",\"iat\":" + now + ",\"exp\":" + expiry + "}").getBytes()

    );



    String signature = createSignature(header + "." + payload);

    return header + "." + payload + "." + signature;

}



public boolean validateToken(String token) {

    try {

        String[] parts = token.split("\\.");

        String signature = createSignature(parts[0] + "." + parts[1]);

        return signature.equals(parts[2]);

    } catch (Exception e) {

        return false;

    }

}



private String createSignature(String data) {

    try {

        Mac mac = Mac.getInstance(ALGORITHM);

        mac.init(new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM));

        return Base64.getEncoder().encodeToString(mac.doFinal(data.getBytes()));

    } catch (Exception e) {

        throw new RuntimeException("Error creating signature", e);

    }

}

}

Login Controller:

@Controller

public class LoginController {

private final JwtUtil jwtUtil;



public LoginController(JwtUtil jwtUtil) {

    this.jwtUtil = jwtUtil;

}



@GetMapping("/login")

public String loginPage() {

    return "login";

}



@PostMapping("/login")

public String login(String username, String password, HttpServletResponse response, Model model) {

    if ("user".equals(username) && "password".equals(password)) {

        String token = jwtUtil.generateToken(username);

        Cookie jwtCookie = new Cookie("JWT", token);

        jwtCookie.setHttpOnly(true);

        jwtCookie.setPath("/");

        jwtCookie.setMaxAge(3600);

        response.addCookie(jwtCookie);

        return "redirect:/home";

    }

    model.addAttribute("error", "Invalid username or password");

    return "login";

}



@GetMapping("/home")

public String home() {

    return "home";

}

}

Thymeleaf Templates:

login.html:

<title>Login</title>
<h1>Login</h1>

<p th:if="${param.expired}" style="color:red;">Your session has expired due to login from another device.</p>

<form th:action="@{/login}" method="post">

    <label for="username">Username:</label>

    <input type="text" id="username" name="username" required><br>

    <label for="password">Password:</label>

    <input type="password" id="password" name="password" required><br>

    <button type="submit">Login</button>

</form>

home.html:

<title>Home</title>
<h1>Welcome to Home Page</h1>

<form th:action="@{/logout}" method="post">

    <button type="submit">Logout</button>

</form>

</htm l>

本文标签: