Spring Security Remember Me Beispiel

Spring Security Remember Me Beispiel

spring-security-remember-me

In diesem Tutorial zeigen wir Ihnen, wie Sie die Anmeldefunktion "Angemeldet bleiben" in Spring Security implementieren. Das bedeutet, dass sich das System an den Benutzer erinnert und auch nach Ablauf der Benutzersitzung eine automatische Anmeldung durchführt.

Verwendete Technologien und Werkzeuge:

  1. Feder 3.2.8.FREIGABE

  2. Federsicherheit 3.2.3.FREIGABE

  3. Spring JDBC 3.2.3.RELEASE

  4. Eclipse 4.2

  5. JDK 1.6

  6. Maven 3

  7. MySQL Server 5.6

  8. Tomcat 6 und 7 (Servlet 3.x)

  9. Testen Sie mit Google Chrome

Einige kurze Notizen:

  1. In Spring Security gibt es zwei Ansätze, um "Remember Me" zu implementieren - Simple Hash-Based Token und Persistent Token Approach.

  2. Um zu verstehen, wie das "Erinnere dich an mich" funktioniert, lesen Sie bitte diese Artikel -Spring remember me reference,Persistent Login Cookie Best Practice,Improved Persistent Login Cookie Best Practice.

  3. In diesem Beispiel wird der "Persistent Token Approach" verwendet. SiehePersistentTokenBasedRememberMeServicesvon Spring.

  4. In diesem Beispiel werden MySQL und die Datenbankauthentifizierung (über Spring JDBC) verwendet.

  5. Die Tabelle "persistent_logins" wird erstellt, um das Anmeldetoken und die Serie zu speichern.

Projekt-Workflows:

  1. Wenn die Benutzeranmeldung mit einem Häkchen bei "Erinnere dich an mich" aktiviert ist, speichert das System ein "Erinnere dich an mich" -Cookie im angeforderten Browser.

  2. Wenn der Browser des Benutzers ein gültiges Cookie zum Erinnern an mich bereitstellt, führt das System eine automatische Anmeldung durch.

  3. Wenn sich der Benutzer über "Erinnere dich an mich" -Cookies anmeldet, muss er zum Aktualisieren der Benutzerdetails den Benutzernamen und das Kennwort erneut eingeben (bewährte Methode, um die Aktualisierung der Benutzerinformationen durch gestohlene Cookies zu verhindern).

P.S This is a very high level of how “remember me” should work, for detail, please refer to the above links in “quick notes”.

1. Projekt Demo

2. Projektverzeichnis

Überprüfen Sie die Projektverzeichnisstruktur.

spring-security-remember-me-directory

3. MySQL-Skripte

SQL-Skripte zum Erstellen vonusers,user_roles undpersistent_logins.

CREATE  TABLE users (
  username VARCHAR(45) NOT NULL ,
  password VARCHAR(45) NOT NULL ,
  enabled TINYINT NOT NULL DEFAULT 1 ,
  PRIMARY KEY (username));

CREATE TABLE user_roles (
  user_role_id int(11) NOT NULL AUTO_INCREMENT,
  username varchar(45) NOT NULL,
  role varchar(45) NOT NULL,
  PRIMARY KEY (user_role_id),
  UNIQUE KEY uni_username_role (role,username),
  KEY fk_username_idx (username),
  CONSTRAINT fk_username FOREIGN KEY (username) REFERENCES users (username));

INSERT INTO users(username,password,enabled)
VALUES ('example','123456', true);

INSERT INTO user_roles (username, role)
VALUES ('example', 'ROLE_USER');
INSERT INTO user_roles (username, role)
VALUES ('example', 'ROLE_ADMIN');

CREATE TABLE persistent_logins (
    username varchar(64) not null,
    series varchar(64) not null,
    token varchar(64) not null,
    last_used timestamp not null,
    PRIMARY KEY (series)
);

4. Angemeldet bleiben (XML-Beispiel)

Um "Remember Me" in der XML-Konfiguration zu aktivieren, fügen Sie dasremember-me-Tag wie folgt in diehttp ein:

spring-security.xml

  
  
    

    

    
    

    
    

  

spring-database.xml

  

    
    
    
    
  

  
  
  
    
  
  1. token-validity-seconds - Das Ablaufdatum des Cookies "Remember-Me" in Sekunden. Zum Beispiel 1209600 = 2 Wochen (14 Tage), 86400 = 1 Tag, 18000 = 5 Stunden.

  2. remember-me-parameter - Der Name des Kontrollkästchens. Der Standardwert ist "_spring_security_remember_me".

  3. data-source-ref - Wenn dies angegeben ist, wird "Persistent Token Approach" verwendet. Der Standardwert ist "Simple Hash-Based Token Approach".

5. Angemeldet bleiben (Beispiel für Anmerkungen)

Das Äquivalent von Anmerkungen:

SecurityConfig.java

package com.example.config;

import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    DataSource dataSource;
    //...

    @Override
    protected void configure(HttpSecurity http) throws Exception {

      http.authorizeRequests()
          .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
        .and()
          .formLogin()
            .successHandler(savedRequestAwareAuthenticationSuccessHandler())
        .loginPage("/login")
            .failureUrl("/login?error")
        .loginProcessingUrl("/auth/login_check")
        .usernameParameter("username")
        .passwordParameter("password")
        .and()
        .logout().logoutSuccessUrl("/login?logout")
        .and()
            .csrf()
        .and()
        .rememberMe().tokenRepository(persistentTokenRepository())
        .tokenValiditySeconds(1209600);
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
        db.setDataSource(dataSource);
        return db;
    }

    @Bean
    public SavedRequestAwareAuthenticationSuccessHandler
                savedRequestAwareAuthenticationSuccessHandler() {

               SavedRequestAwareAuthenticationSuccessHandler auth
                    = new SavedRequestAwareAuthenticationSuccessHandler();
        auth.setTargetUrlParameter("targetUrl");
        return auth;
    }

}

P.S In annotation configuration, the default http name for “remember me” check box is “remember-me”.

6.HTML / JSP Pages

6.1 In JSP, you can use Spring security tag sec:authorize access="isRememberMe()" to determine if this user is login by “remember me” cookies.

admin.jsp

<%@taglib prefix="sec"
    uri="http://www.springframework.org/security/tags"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page session="true"%>


    

Title : ${title}

Message : ${message}

Welcome : ${pageContext.request.userPrincipal.name} | Logout

# This user is login by "Remember Me Cookies".

# This user is login by username / password.

6.2 A simple login form with “remember me” check box.

login.jsp

   
User:
Password:
Remember Me:

6.3 Update page. Nur Benutzer, die sich mit einem Passwort anmelden, dürfen auf diese Seite zugreifen.

update.jsp

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page session="true"%>


    

Title : Spring Security Remember Me Example - Update Form

Message : This page is for ROLE_ADMIN and fully authenticated only (Remember me cookie is not allowed!)

Update Account Information...

7. Regler

Spring-Controller-Klasse, lesen Sie den Kommentar für selbsterklärende.

MainController.java

package com.example.web.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MainController {

    @RequestMapping(value = { "/", "/welcome**" }, method = RequestMethod.GET)
    public ModelAndView defaultPage() {

        ModelAndView model = new ModelAndView();
        model.addObject("title", "Spring Security Remember Me");
        model.addObject("message", "This is default page!");
        model.setViewName("hello");
        return model;

    }

    @RequestMapping(value = "/admin**", method = RequestMethod.GET)
    public ModelAndView adminPage() {

        ModelAndView model = new ModelAndView();
        model.addObject("title", "Spring Security Remember Me");
        model.addObject("message", "This page is for ROLE_ADMIN only!");
        model.setViewName("admin");

        return model;

    }

    /**
     * This update page is for user login with password only.
     * If user is login via remember me cookie, send login to ask for password again.
     * To avoid stolen remember me cookie to update info
     */
    @RequestMapping(value = "/admin/update**", method = RequestMethod.GET)
    public ModelAndView updatePage(HttpServletRequest request) {

        ModelAndView model = new ModelAndView();

        if (isRememberMeAuthenticated()) {
            //send login for update
            setRememberMeTargetUrlToSession(request);
            model.addObject("loginUpdate", true);
            model.setViewName("/login");

        } else {
            model.setViewName("update");
        }

        return model;

    }

    /**
     * both "normal login" and "login for update" shared this form.
     *
     */
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public ModelAndView login(@RequestParam(value = "error", required = false) String error,
      @RequestParam(value = "logout", required = false) String logout,
          HttpServletRequest request) {

        ModelAndView model = new ModelAndView();
        if (error != null) {
            model.addObject("error", "Invalid username and password!");

            //login form for update page
                        //if login error, get the targetUrl from session again.
            String targetUrl = getRememberMeTargetUrlFromSession(request);
            System.out.println(targetUrl);
            if(StringUtils.hasText(targetUrl)){
                model.addObject("targetUrl", targetUrl);
                model.addObject("loginUpdate", true);
            }

        }

        if (logout != null) {
            model.addObject("msg", "You've been logged out successfully.");
        }
        model.setViewName("login");

        return model;

    }

    /**
     * Check if user is login by remember me cookie, refer
     * org.springframework.security.authentication.AuthenticationTrustResolverImpl
     */
    private boolean isRememberMeAuthenticated() {

        Authentication authentication =
                    SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return false;
        }

        return RememberMeAuthenticationToken.class.isAssignableFrom(authentication.getClass());
    }

    /**
     * save targetURL in session
     */
    private void setRememberMeTargetUrlToSession(HttpServletRequest request){
        HttpSession session = request.getSession(false);
        if(session!=null){
            session.setAttribute("targetUrl", "/admin/update");
        }
    }

    /**
     * get targetURL from session
     */
    private String getRememberMeTargetUrlFromSession(HttpServletRequest request){
        String targetUrl = "";
        HttpSession session = request.getSession(false);
        if(session!=null){
            targetUrl = session.getAttribute("targetUrl")==null?""
                             :session.getAttribute("targetUrl").toString();
        }
        return targetUrl;
    }

}

8. Demo

8.1 Access protected page – http://localhost:8080/spring-security-remember-me/admin, the system will redirect user to a login form. Versuchen Sie sich mit aktiviertem "Remember Me" anzumelden.

spring-security-remember-me-example-0

spring-security-remember-me-example-2

8.2 In Google Chrome, Settings → Show advanced settings → Privacy, Content Setting… → “All cookies and site data” – there are two cookies for localhost, one for current session and one for “remember me” login cookies.

spring-security-remember-me-example-1

8.3 Review table “persistent_logins”, username, series and token is stored.

spring-security-remember-me-table

8.4 Restart the web application, go Chrome “All cookies and site data”, and remove the browser’s session “JSESSIONID”. Versuchen Sie erneut, auf die Anmeldeseite zuzugreifen. Das System wird sich nun an Sie erinnern und sich automatisch über die Login-Cookies in Ihrem Browser anmelden.

spring-security-remember-me-example-3

8.5 Try to access the “update” page – http://localhost:8080/spring-security-remember-me/admin/update, if user is login by remember me cookies, the system will redirect user to login form again. Dies ist eine gute Vorgehensweise, um zu vermeiden, dass gestohlene Cookies Benutzerdetails aktualisieren.

spring-security-remember-me-example-4

8.6 Done.

spring-security-remember-me-example-5

9. Misc

Einige wichtige Spring Security-Kurse zum Lernen:

  1. org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer.java

  2. org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices.java

  3. org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices.java

  4. org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.java

  5. org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter

Quellcode herunterladen

Laden Sie es herunter -spring-security-remember-me.zip (18 KB)

Laden Sie es herunter -spring-security-remember-me-annotation.zip (25 KB)