Spring Security Remember Me Hashing Authentication Example

摘要: In this tutorial we demonstrate how to create a Spring Security Remember Me Hashing Authentication application. Remember me authentication is a feature that allows web sites to remember the identity of a user between sessions. Spring security provides two remember-me implementation. One uses hashing to preserve the security of cookie-based tokens which we’ll tackle in this tutorial. The second uses a database or other persistent storage mechanism to store the generated tokens.

In this tutorial we demonstrate how to create a Spring Security Remember Me Hashing Authentication application. Remember me authentication is a feature that allows web sites to remember the identity of a user between sessions. Spring security provides two remember-me implementation. One uses hashing to preserve the security of cookie-based tokens which we’ll tackle in this tutorial. The second uses a database or other persistent storage mechanism to store the generated tokens.

Remember Me Hashed Based Token Approach

When the user enables remember me authentication, a cookie is created and passed upon subsequent logins. This approach uses hashing to achieve a useful remember-me strategy. The cookie is composed as follows:

base64(username + ":" + expirationTime + ":" +
md5Hex(username + ":" + expirationTime + ":" password + ":" + key))

username:          As identifiable to the UserDetailsService
password:          That matches the one in the retrieved UserDetails
expirationTime:    The date and time when the remember-me token expires, expressed in milliseconds
key:               A private key to prevent modification of the remember-me token

Note: The remember me token is valid only for the specified expirationTime and provided that the username, password and key does not change. Warning: This is a potentially security issue. When the remember me token is captured by a malicious user-agent this user-agent is able to use the token until it expires. If better security is needed you should use the persistent remember-me token approach.

Project Structure

Let’s start by looking at the project structure.

Maven Dependencies

We use Apache Maven to manage our project dependencies. Make sure the following dependencies reside on the class-path.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.memorynotfound.spring.security</groupId>
    <artifactId>hashing-remember-me</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <url>https://memorynotfound.com</url>
    <name>Spring Security - ${project.artifactId}</name>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
        </dependency>

        <!-- bootstrap and jquery -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>3.3.7</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.2.1</version>
        </dependency>

        <!-- testing -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Spring Security Remember Me Hashing Authentication Configuration

To enable remember me hashing authentication configuration we need to register this with spring. In the following section, we demonstrate both Java -and XML Configuration.

Spring Java Configuration

Here we are configuring the Remember Me authentication using Java Configuration. By extending our Spring Configuration class with WebSecurityConfigurerAdapter we can simply configure the remember me authentication in the configure(HttpSecurity http) method. We need to configure a secure and unique key. This key is typically a strong and unique cipher. We can optionally configure the remember me cookie name and set the tokens validity period. This defaults to 2 weeks.

package com.memorynotfound.spring.security.config;

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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers(
                        "/js/**",
                        "/css/**",
                        "/img/**",
                        "/webjars/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                    .anyRequest().authenticated()
                .and()
                    .formLogin()
                        .loginPage("/login")
                            .permitAll()
                .and()
                    .logout()
                        .invalidateHttpSession(true)
                        .clearAuthentication(true)
                        .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                        .logoutSuccessUrl("/login?logout")
                            .permitAll()
                .and()
                    .rememberMe()
                        .key("unique-and-secret")
                        .rememberMeCookieName("remember-me-cookie-name")
                        .tokenValiditySeconds(24 * 60 * 60);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
    }

}
Spring XML Configuration

The spring-security-config.xml is located in the src/main/resources/ folder and is the equivalent Spring XML Configuration.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xsi:schemaLocation="http://www.springframework.org/schema/security
                                 http://www.springframework.org/schema/security/spring-security.xsd
                                 http://www.springframework.org/schema/beans
                                 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <http>
        <intercept-url pattern="/login" access="permitAll()"/>
        <intercept-url pattern="/js/**" access="permitAll()"/>
        <intercept-url pattern="/css/**" access="permitAll()"/>
        <intercept-url pattern="/img/**" access="permitAll()"/>
        <intercept-url pattern="/webjars/**" access="permitAll()"/>
        <intercept-url pattern="/**" access="isAuthenticated()"/>

        <form-login 
                login-page="/login"/>
        <logout
                invalidate-session="true"
                logout-url="/logout"
                logout-success-url="/login?logout"/>
        <remember-me
                key="unique-and-secret"
                remember-me-cookie="remember-me-cookie-name"
                token-validity-seconds="86400"/>
    </http>

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="user"
                      password="password"
                      authorities="ROLE_USER" />
            </user-service>
        </authentication-provider>
    </authentication-manager>

</beans:beans>

Creating Controller

We created some simple navigation controllers.

package com.memorynotfound.spring.security.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

    @GetMapping("/")
    public String greeting(){
        return "index";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

Spring Boot

We use Spring Boot to start our application.

package com.memorynotfound.spring.security;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
// uncomment if you want to use Spring Security XML Configuration
// @ImportResource("classpath:spring-security-config.xml")
public class Run {

    public static void main(String[] args) {
        SpringApplication.run(Run.class, args);
    }
}

Thymeleaf Templates

We used Thymeleaf to create our views. These templates uses bootstrap and jquery loaded from the org.webjars.

Creating the login page

The login.html page is located in the src/main/resources/templates folder. In the form we created a remember-me checkbox. When the user enables remember me authentication a cookie is passed to the browser which will expire in the specified amount. When the user accesses the page between sessions, Spring Security automatically authenticates the uses based on the remember-me token.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>

    <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.min.css}"/>
    <link rel="stylesheet" type="text/css" th:href="@{/css/main.css}"/>

    <title>Login</title>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-4 col-md-offset-4">
            <div class="panel panel-default">
                <div class="panel-body">
                    <div class="text-center">
                        <h3><i class="glyphicon glyphicon-lock" style="font-size:2em;"></i></h3>
                        <h2 class="text-center">Login</h2>
                        <div class="panel-body">

                            <div th:if="${param.error}">
                                <div class="alert alert-danger">
                                    Invalid username or password.
                                </div>
                            </div>
                            <div th:if="${param.logout}">
                                <div class="alert alert-info">
                                    You have been logged out.
                                </div>
                            </div>

                            <form th:action="@{/login}" method="post">
                                <div class="form-group">
                                    <div class="input-group">
                                        <span class="input-group-addon">@</span>
                                        <input id="username"
                                               name="username"
                                               autofocus="autofocus"
                                               class="form-control"
                                               placeholder="Username"/>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <div class="input-group">
                                        <span class="input-group-addon">
                                            <i class="glyphicon glyphicon-lock"></i>
                                        </span>
                                        <input id="password"
                                               name="password"
                                               class="form-control"
                                               placeholder="Password"
                                               type="password"/>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <label>
                                        <input id="remember-me"
                                               name="remember-me"
                                               type="checkbox"/> Remember me
                                    </label>
                                </div>
                                <div class="form-group">
                                    <button type="submit" class="btn btn-success btn-block">Login</button>
                                </div>
                            </form>

                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script type="text/javascript" th:src="@{/webjars/jquery/3.2.1/jquery.min.js/}"></script>
<script type="text/javascript" th:src="@{/webjars/bootstrap/3.3.7/js/bootstrap.min.js}"></script>

</body>
</html>

Creating a secured page

The index.html page is located in the src/main/resources/templates folder.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>

    <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.min.css}"/>
    <link rel="stylesheet" type="text/css" th:href="@{/css/main.css}"/>

    <title>Registration</title>
</head>
<body>
<div class="container">
    <h1>Spring Security Remember Me Hashing Configuration Example</h1>

    <div sec:authorize="isRememberMe()">
        The user: <span sec:authentication="name"></span> is logged in by "Remember Me Cookies".
    </div>

    <div sec:authorize="isFullyAuthenticated()">
        The user: <span sec:authentication="name"></span> is logged in by "Username / Password".
    </div>

</div>
<footer>
    <div class="container">
        <p>
            &copy; Memorynotfound.com
            <span sec:authorize="isAuthenticated()" style="display: inline-block;">
                    | Logged user: <span sec:authentication="name"></span> |
                    Roles: <span sec:authentication="principal.authorities"></span> |
                    <a th:href="@{/logout}">Sign Out</a>
            </span>
        </p>
    </div>
</footer>

<script type="text/javascript" th:src="@{/webjars/jquery/3.2.1/jquery.min.js/}"></script>
<script type="text/javascript" th:src="@{/webjars/bootstrap/3.3.7/js/bootstrap.min.js}"></script>

</body>
</html>

Demo

Access http://localhost:8080 and page is redirected to http://localhost:8080/login.

When user and password are correctly filled, the page is redirected to http://localhost:8080/.

When inspecting the cookies of our application, we can see Spring created the remember-me-cookie-name which we configured earlier.

When you delete the JSESSIONID cookie and refresh the page, you’re automatically logged in when the remember-me cookie isn’t expired.

Download

上一篇: 没有了
下一篇: Spring Security In Memory Authentication Example
 评论 ( What Do You Think )
名称
邮箱
网址
评论
验证
   
 

 


  • 微信公众号

  • 我的微信

站点声明:

1、一号门博客CMS,由Python, MySQL, Nginx, Wsgi 强力驱动

2、部分文章或者资源来源于互联网, 有时候很难判断是否侵权, 若有侵权, 请联系邮箱:summer@yihaomen.com, 同时欢迎大家注册用户,主动发布无版权争议的 文章/资源.

3、鄂ICP备14001754号-3, 鄂公网安备 42280202422812号