Histoire de base et systématique Histoire de certification / autorisation Histoire Remember-Me Histoire CSRF L'histoire de l'en-tête de la réponse Histoire de la sécurité de la méthode Histoire CORS L'histoire de Run-As Histoire ACL Test story Parlez de la coopération avec MVC et Boot
Edition supplémentaire Ce que Spring Security peut et ne peut pas faire
Par défaut, le même utilisateur peut créer plusieurs sessions. En d'autres termes, un même utilisateur peut se connecter autant de fois qu'il le souhaite à partir de différents terminaux.
En modifiant les paramètres, vous pouvez limiter le nombre de sessions simultanées ou empêcher les connexions multiples du tout.
namespace
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
</web-app>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<sec:http>
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login />
<sec:logout />
<sec:session-management>
<sec:concurrency-control max-sessions="1" />
</sec:session-management>
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="hoge" password="hoge" authorities="" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
Java Configuration
MySpringSecurityInitializer.java
package sample.spring.security;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class MySpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public MySpringSecurityInitializer() {
super(MySpringSecurityConfig.class);
}
@Override
protected boolean enableHttpSessionEventPublisher() {
return true;
}
}
MySpringSecurityConfig.java
package sample.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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 java.util.Collections;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.sessionManagement().maximumSessions(1);
}
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("hoge")
.password("hoge")
.authorities(Collections.emptyList());
}
}
Connectez-vous avec le premier navigateur.
Connectez-vous avec le même utilisateur dans un autre navigateur.
Lorsque je réaffiche le premier navigateur, un message d'erreur s'affiche.
web.xml
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
MySpringSecurityInitializer.java
@Override
protected boolean enableHttpSessionEventPublisher() {
return true;
}
HttpSessionEventPublisher
en tant qu'écouteur pour que Spring Security puisse détecter qu'une nouvelle session a été créée.
--Si vous utilisez web.xml
, déclarez-le normalement avec <auditeur>
.WebApplicationInitializer
, vous pouvez remplacer la méthode ʻenableHttpSessionEventPublisher ()pour retourner
true et elle enregistrera
HttpSessionEventPublisher` comme écouteur.applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<sec:http>
...
<sec:session-management>
<sec:concurrency-control max-sessions="1" />
</sec:session-management>
</sec:http>
...
</beans>
--Ajoutez la balise <session-management>
, ajoutez la balise <concurrency-control>
et spécifiez le nombre maximum de sessions avec l'attribut max-sessions
.
MySpringSecurityConfig.java
...
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.and()
.sessionManagement().maximumSessions(1);
}
...
}
--Spécifiez avec sessionManagement (). MaximumSessions (int)
.
Par défaut, l'écran n'affichera qu'un message d'erreur, alors passez à la page spécifiée (par exemple, la page de connexion).
namespace
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<sec:http>
...
<sec:session-management >
<sec:concurrency-control max-sessions="1" expired-url="/login" />
</sec:session-management>
</sec:http>
...
</beans>
--Spécifiez l'URL et le chemin dans l'attribut ʻexpired-url`.
Java Configuration
MySpringSecurityConfig.java
...
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.and()
.sessionManagement().maximumSessions(1).expiredUrl("/login");
}
...
}
--Spécifié par la méthode ʻexpiredUrl (String) `.
MySessionInformationExpiredStrategy.java
package sample.spring.security.session;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import javax.servlet.ServletException;
import java.io.IOException;
public class MySessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
redirectStrategy.sendRedirect(event.getRequest(), event.getResponse(), "/login");
}
}
--Créez une classe qui implémente SessionInformationExpiredStrategy
.
La méthode --ʻOnExpiredSessionDetected () `implémente le traitement des erreurs.
/ login
.namespace
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<bean id="sies" class="sample.spring.security.session.MySessionInformationExpiredStrategy" />
<sec:http>
...
<sec:session-management >
<sec:concurrency-control max-sessions="1" expired-session-strategy-ref="sies" />
</sec:session-management>
</sec:http>
...
</beans>
--Spécifiez le bean de SessionInformationExpiredStrategy
avec ʻexpired-session-strategy-ref`.
Java Configuration
MySpringSecurityConfig.java
...
import sample.spring.security.session.MySessionInformationExpiredStrategy;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.and()
.sessionManagement()
.maximumSessions(1)
.expiredSessionStrategy(new MySessionInformationExpiredStrategy());
}
...
}
―― Enregistrez-vous avec la méthode ʻexpiredSessionStrategy (SessionInformationExpiredStrategy) `.
Si vous spécifiez simplement max-sessions
( maximumSessions
), si vous vous connectez au-delà de la limite supérieure, la session la plus ancienne sera supprimée en premier.
Inversement, si vous essayez de vous connecter au-delà de la limite, vous pouvez faire une erreur si vous essayez de créer une nouvelle session. Cependant, sachez que si vous activez ce paramètre, vous ne pourrez peut-être pas vous reconnecter jusqu'à ce que votre ancienne session expire ou que vous vous déconnectiez explicitement.
namespace
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<sec:http>
...
<sec:session-management >
<sec:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</sec:session-management>
</sec:http>
...
</beans>
--Spécifiez «true» pour «erreur-si-maximum-dépassé».
Java Configuration
MySpringSecurityConfig.java
...
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.and()
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true);
}
...
}
true
dans la méthode maxSessionsPreventsLogin (boolean)
.** Contrôle de fonctionnement **
Connectez-vous avec le premier navigateur.
Si vous essayez de vous connecter avec le même utilisateur sur le deuxième navigateur, une erreur se produit.
max-session-error.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Max Session</title>
</head>
<body>
<h1>Le nombre de sessions a atteint la limite</h1>
<%@page import="org.springframework.security.core.AuthenticationException" %>
<%@page import="org.springframework.security.web.WebAttributes" %>
<%
AuthenticationException e = (AuthenticationException)session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
pageContext.setAttribute("errorMessage", e.getMessage());
%>
<h2 style="color: red;">${errorMessage}</h2>
</body>
</html>
La page qui sera la destination de la transition. Un message d'erreur s'affiche.
namespace
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<sec:http>
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/max-session-error.jsp" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login authentication-failure-url="/max-session-error.jsp" />
<sec:logout />
<sec:session-management>
<sec:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</sec:session-management>
</sec:http>
...
</beans>
Java Configuration
MySpringSecurityConfig.java
...
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/max-session-error.jsp").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.failureUrl("/max-session-error.jsp")
.and()
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true);
}
...
}
Connectez-vous avec le premier navigateur.
Lorsque vous vous connectez avec le deuxième navigateur, vous serez redirigé vers la page d'erreur spécifiée (max-session-error.jsp
).
applicationContext.xml
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/max-session-error.jsp" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login authentication-failure-url="/max-session-error.jsp" />
de la balise
dans ʻauthentication-failure-handler-ref
(pour ʻAuthenticationFailureHandler`](http://qiita.com/opengl- " 8080 / items / 032ed0fa27a239bdc1cc #% E3% 83% AD% E3% 82% B0% E3% 82% A4% E3% 83% B3% E3% 81% AB% E5% A4% B1% E6% 95% 97% E3 % 81% 97% E3% 81% 9F% E3% 81% A8% E3% 81% 8D% E3% 81% AE% E5% 87% A6% E7% 90% 86% E3% 82% 92% E5% AE % 9F% E8% A3% 85% E3% 81% 99% E3% 82% 8B))SessionAuthenticationException
est levée.Pourquoi la spécification de la destination de transition lorsqu'une erreur se produit dans le contrôle du nombre maximum de sessions est-elle définie sur la balise <form-login>
?
applicationContext.xml
<sec:session-management>
<sec:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</sec:session-management>
--Lorsque vous ajoutez la balise <session-management>
, un filtre appelé SessionManagementFilter
est ajouté.
SessionAuthenticationStrategy
que ce filtre a.ConcurrentSessionControlAuthenticationStrategy
.
--Cependant, cette SessionAuthenticationStrategy
a également un ʻUsernamePasswordAuthenticationFilter` qui exécute le processus de connexion par formulaire.est réussie, le processus d'authentification lié à la session est délégué à
SessionAuthenticationStrategy`.. --
SessionManagementFilter attend après ʻUsernamePasswordAuthenticationFilter
.
--Par conséquent, le paramètre de connexion par formulaire sera adopté pour la spécification de page d'erreur qui dépasse le nombre maximal de sessions.--SessionManagementFilter
est en charge du traitement lorsqu'il est connecté par un traitement d'authentification non interactif.
Ceci est une estimation personnelle et je ne sais pas si c'est correct </ font>
SessionManagementFilter
est un filtre après ʻUsernamePasswordAuthenticationFilter, il n'est pas possible de confier le contrôle de session au moment de la connexion par formulaire à
SessionManagementFilter`.SessionManagementFilter
avant ʻUsernamePasswordAuthenticationFilter? J'ai pensé, mais c'est peut-être parce qu'il est accablant de mettre
SessionManagementFilter, qui devrait être vérifié après une connexion réussie, avant ʻUsernamePasswordAuthenticationFilter
où le processus de connexion est effectué.La mise en oeuvre
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<sec:http>
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login />
<sec:remember-me />
<sec:logout />
<sec:session-management>
<sec:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</sec:session-management>
</sec:http>
...
</beans>
** Résultat de l'opération **
Connectez-vous avec Remember-Me activé (navigateur A).
Laissez-le pendant un moment et attendez que la session expire (en supposant que le jeton Remember-Me n'a pas expiré).
Connectez-vous avec un autre navigateur (navigateur B) et redessinez l'écran avec le navigateur timeout (navigateur A).
Vous êtes passé à l'écran d'erreur 401 (navigateur A).
Par défaut, si Remember-Me ne dépasse pas la limite de session, il en résultera une erreur 401 comme décrit ci-dessus et sera ignoré à la page d'erreur du serveur.
Pour passer à n'importe quel chemin, la référence indique que vous devez spécifier l'attribut session-authentication-error-url
de la balise <session-management>
.
If the second authentication takes place through another non-interactive mechanism, such as "remember-me", an "unauthorized" (401) error will be sent to the client. If instead you want to use an error page, you can add the attribute session-authentication-error-url to the session-management element. (Traduction) Si la deuxième authentification est effectuée par un autre mécanisme non interactif (par exemple Remember-Me) (autre que la connexion par formulaire), une erreur 401 non autorisée sera envoyée au client. Si vous souhaitez utiliser la page d'erreur à la place, vous pouvez ajouter l'attribut
session-authentication-error-url
à l'élémentsession-management
.
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#ns-concurrent-sessions
Cependant, si vous spécifiez réellement ce chemin, une boucle de redirection se produira.
La mise en oeuvre
my-login.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>My Login Page</title>
</head>
<body>
<h1>My Login Page</h1>
<c:url var="loginUrl" value="/login" />
<form action="${loginUrl}" method="post">
<div>
<label>Nom d'utilisateur: <input type="text" name="username" /></label>
</div>
<div>
<label>mot de passe: <input type="password" name="password" /></label>
</div>
<div>
<label>Remember-Me : <input type="checkbox" name="remember-me"></label>
</div>
<input type="submit" value="login" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<sec:http>
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/my-login.jsp" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login login-page="/my-login.jsp" />
<sec:remember-me />
<sec:logout />
<sec:session-management session-authentication-error-url="/my-login.jsp">
<sec:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</sec:session-management>
</sec:http>
...
</beans>
** Résultat de l'opération **
Dans le cas de Remember-Me, le nombre de sessions est vérifié avec SessionManagementFilter
.
Ici, la redirection est effectuée vers l'URL spécifiée par session-authentication-error-url
.
Ensuite, une fois redirigé vers l'URL, une série de filtres sera à nouveau exécutée, et Remember-Me se connectera automatiquement et SessionManagementFilter
vérifiera le nombre de sessions.
Naturellement, l'erreur se reproduit et la redirection est effectuée vers l'URL spécifiée par session-authentication-error-url
.
En conséquence, une boucle de redirection se produit.
J'ai cherché, mais je n'ai trouvé aucun problème avec la même histoire. Je ne trouve pas de solution de contournement.
Suis-je mal compris quelque chose ...?
En passant, ce problème ne se produit pas si vous utilisez la page de connexion que Spring Security génère par défaut.
Ceci est évité car le DefaultLoginPageGeneratingFilter
existe avant le SessionManagementFilter
, donc la page de connexion par défaut est transférée avant que le contrôle du nombre de sessions ne soit répété.
Pour une description de l'attaque de fixation de session elle-même, voir page d'explication IPA.
En guise de contre-mesure, il est recommandé de modifier l'ID de session avant et après la connexion.
Spring Security a également cette contre-mesure activée par défaut, et l'ID de session (JSESSIONID
) est modifié avant et après la connexion.
JSESSIONID
avant la connexion est 376D515 ...
s'identifier
Vous pouvez voir que le JSESSIONID
après la connexion est devenu 30194EA ...
.
Pour le moment, la modification ou non de l'ID de session après cette connexion peut être modifiée dans les paramètres.
namespace
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<sec:http>
...
<sec:session-management session-fixation-protection="none" />
</sec:http>
...
</beans>
--Spécifiez l'attribut session-fixation-protection
de <session-management>
Java Configuration
MySpringSecurityConfig.java
...
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.and()
.sessionManagement()
.sessionFixation().none();
}
...
}
sessionFixation (). None ()
Avant de vous connecter 8CC8D2 ...
Même après la connexion à 8CC8D2 ...
Cela n'a pas changé. Cependant, je ne peux pas imaginer une situation où vous devez spécifier «aucun». .. ..
Vous pouvez spécifier les valeurs suivantes pour session-fixation-protection
:
valeur | La description |
---|---|
none |
Ne changez pas l'ID de session |
newSession |
Créez une nouvelle session. Tous les attributs autres que ceux gérés par Spring Security sont supprimés. |
migrateSession |
Créez également une nouvelle session et des attributs de port (Servlet 3).Valeur par défaut dans les environnements inférieurs à 0) |
changeSessionId |
HttpServletRequest Ajouté àchangeSessionId() Réaffecter l'ID avec (Servlet 3).Par défaut dans les environnements 1 et supérieurs) |
Recommended Posts