TokiNoviceProgrammer / mrs-for-spring-boot

0 stars 0 forks source link

spring security ログインセッションの設定 #10

Open TokiNoviceProgrammer opened 2 weeks ago

TokiNoviceProgrammer commented 2 weeks ago

https://spring.pleiades.io/spring-security/reference/servlet/authentication/session-management.html

TokiNoviceProgrammer commented 2 weeks ago
TokiNoviceProgrammer commented 1 week ago

ユーザーがページを離れる前に /logout エンドポイントにログアウトリクエストを送信

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Logout on Tab Close</title>
    <meta name="_csrf" content="${_csrf.token}"/>
    <meta name="_csrf_header" content="${_csrf.headerName}"/>
    <script>
        window.addEventListener("beforeunload", function (event) {
            var csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
            var csrfHeader = document.querySelector('meta[name="_csrf_header"]').getAttribute('content');

            var headers = {};
            headers[csrfHeader] = csrfToken;

            var formData = new FormData();
            formData.append(csrfHeader, csrfToken);

            navigator.sendBeacon("/logout", formData);
        });
    </script>
</head>
<body>
    <h1>Welcome to the Application</h1>
</body>
</html>
TokiNoviceProgrammer commented 1 week ago
window.addEventListener("beforeunload", function (event) {
    var csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
    var csrfHeader = document.querySelector('meta[name="_csrf_header"]').getAttribute('content');

    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/logout", false); // 同期リクエスト
    xhr.setRequestHeader(csrfHeader, csrfToken);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.send();
});
TokiNoviceProgrammer commented 1 week ago
window.addEventListener("beforeunload", function (event) {
    var csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
    var csrfHeader = document.querySelector('meta[name="_csrf_header"]').getAttribute('content');

    fetch('/logout', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: encodeURIComponent(csrfHeader) + '=' + encodeURIComponent(csrfToken)
    }).then(response => {
        if (response.ok) {
            console.log('Logged out successfully');
        } else {
            console.error('Logout failed');
        }
    }).catch(error => {
        console.error('Error:', error);
    });
});
TokiNoviceProgrammer commented 1 week ago
window.addEventListener("beforeunload", function (event) {
    var csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
    var csrfHeader = document.querySelector('meta[name="_csrf_header"]').getAttribute('content');

    var headers = {};
    headers[csrfHeader] = csrfToken;

    var data = new URLSearchParams();
    data.append(csrfHeader, csrfToken);

    var blob = new Blob([data.toString()], { type: 'application/x-www-form-urlencoded' });

    navigator.sendBeacon("/test/logout", blob);
});
TokiNoviceProgrammer commented 1 week ago

image

https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.ConcurrencyControlConfigurer.html

TokiNoviceProgrammer commented 1 week ago

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.session.HttpSessionEventPublisher; import org.springframework.security.web.session.SessionRegistry; import org.springframework.security.web.session.SessionRegistryImpl;

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .sessionManagement()
            .maximumSessions(1)
            .sessionRegistry(sessionRegistry());
}

@Bean
public SessionRegistry sessionRegistry() {
    return new SessionRegistryImpl();
}

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
    return new HttpSessionEventPublisher();
}

}

TokiNoviceProgrammer commented 1 week ago

window.onload = function() { if (localStorage.getItem('tabOpened')) { alert('既にこのサイトの他のタブが開かれています。'); window.location.href = 'about:blank'; } else { localStorage.setItem('tabOpened', 'true'); }

window.onbeforeunload = function() {
    localStorage.removeItem('tabOpened');
}

};

TokiNoviceProgrammer commented 1 week ago

import org.springframework.security.web.csrf.CsrfToken; import org.springframework.stereotype.Component; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.io.IOException;

@Component public class CsrfTokenFilter implements Filter {

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    HttpSession session = httpServletRequest.getSession(false);
    if (session != null) {
        CsrfToken csrfToken = (CsrfToken) httpServletRequest.getAttribute(CsrfToken.class.getName());
        if (csrfToken != null) {
            String currentToken = csrfToken.getToken();
            String previousToken = (String) session.getAttribute("previousCsrfToken");

            // セッションに保存されたトークンと一致しない場合はエラーをスロー
            if (previousToken != null && !previousToken.equals(currentToken)) {
                throw new ServletException("Invalid CSRF token. Possible multiple tab usage detected.");
            }

            // セッションに現在のトークンを保存
            session.setAttribute("previousCsrfToken", currentToken);
        }
    }
    chain.doFilter(request, response);
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {}

@Override
public void destroy() {}

}

TokiNoviceProgrammer commented 1 week ago

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.csrf.CsrfFilter;

@Configuration @Order(1) public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private CsrfTokenFilter csrfTokenFilter;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().and()
        .authorizeRequests()
        .anyRequest().authenticated()
        .and()
        .addFilterAfter(csrfTokenFilter, CsrfFilter.class);
}

}

TokiNoviceProgrammer commented 1 week ago

import java.util.UUID;

public class TokenGenerator { public static String generateToken() { return UUID.randomUUID().toString(); }

public static void main(String[] args) {
    String token = generateToken();
    System.out.println("Generated Token: " + token);
}

}

TokiNoviceProgrammer commented 1 week ago

import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;

@Component public class TokenInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 画面からトークンを取得
    String requestToken = request.getParameter("token");

    // ここで、ユーザーの認証情報からトークンを取得する(例:セッション、DB、等)
    String userToken = getUserTokenFromSessionOrDatabase();

    // トークンが一致するか確認
    if (requestToken == null || !requestToken.equals(userToken)) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token");
        return false;
    }

    return true;
}

private String getUserTokenFromSessionOrDatabase() {
    // 実際の実装では、セッションやデータベースからトークンを取得します
    // ここでは仮のトークンを返す
    return "expectedToken";
}

}

TokiNoviceProgrammer commented 1 week ago

import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession;

@Component public class TokenInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // トークンを生成しセッションに保存
    HttpSession session = request.getSession();
    String token = TokenUtil.generateToken();
    session.setAttribute("token", token);
    return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    // モデルにトークンを追加
    if (modelAndView != null) {
        HttpSession session = request.getSession();
        String token = (String) session.getAttribute("token");
        modelAndView.addObject("token", token);
    }
}

}

TokiNoviceProgrammer commented 1 week ago

import java.util.UUID;

public class TokenUtil { public static String generateToken() { return UUID.randomUUID().toString(); } }

TokiNoviceProgrammer commented 1 week ago

import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;

@Controller public class MyController {

@GetMapping("/showForm")
public String showForm(Model model) {
    model.addAttribute("message", "Hello, Thymeleaf!");
    return "form"; // form.html というテンプレートを返す
}

@PostMapping("/submitForm")
public String submitForm(@RequestParam String name, Model model) {
    // ModelをModelMapにキャスト
    if (model instanceof ModelMap) {
        ModelMap modelMap = (ModelMap) model;
        // ModelMapから値を取得
        String message = (String) modelMap.get("message");

        // 値を使って何か処理を行う(例としてコンソールに出力)
        System.out.println("Message: " + message);
        System.out.println("Name: " + name);

        // 処理結果を再度Modelに追加してビューに渡す
        model.addAttribute("result", "Form submitted successfully!");
    }

    return "result"; // result.html というテンプレートを返す
}

}