123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- [[recipe-basic-auth]]
- = Recipe: Basic Auth
- NOTE: You should not use basic auth for projects other than proofs of concept and demonstrations.
- We include it in the cookbook because it lets us show the basic pattern of Spring Security with the fewest additional details.
- (In other words, it is the simplest example.)
- If you are already familiar with Spring Security, you might want to skip this recipe.
- If you are new to Spring Security, this recipe is worth reviewing, to learn the basics.
- [[security-cookbook-the-web-application]]
- == The Application to Secure
- Spring Security secures applications, so we need an application to secure.
- A simple web application suffices as an example that we can then secure in the various recipes.
- NOTE: We use the same example that we used in the "`Securing a Web Application`" guide, which you can find on the Spring web site at https://spring.io/guides/gs/securing-web/[https://spring.io/guides/gs/securing-web/].
- We use Spring Boot with the Spring Web and Thymeleaf dependencies.
- There are lots of ways to make a web application, but we know this one well, since we have documented it elsewhere.
- We start with the build files for both Maven and Gradle (in case you prefer one or the other).
- The following listing shows the build file for Maven (`pom.xml`):
- ====
- [source,xml]
- ----
- <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.2.0.RELEASE</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
- <groupId>com.example</groupId>
- <artifactId>securing-web</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>securing-web</name>
- <description>Demo project for Spring Boot</description>
- <properties>
- <java.version>1.8</java.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-thymeleaf</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>org.junit.vintage</groupId>
- <artifactId>junit-vintage-engine</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
- </project>
- ----
- ====
- The following listing shows the build file for Gradle (`build.gradle`):
- ====
- [source,java]
- ----
- plugins {
- id 'org.springframework.boot' version '2.2.0.RELEASE'
- id 'io.spring.dependency-management' version '1.0.8.RELEASE'
- id 'java'
- }
- group = 'com.example'
- version = '0.0.1-SNAPSHOT'
- sourceCompatibility = '1.8'
- repositories {
- mavenCentral()
- }
- dependencies {
- implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
- implementation 'org.springframework.boot:spring-boot-starter-web'
- testImplementation('org.springframework.boot:spring-boot-starter-test') {
- exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
- }
- }
- test {
- useJUnitPlatform()
- }
- ----
- ====
- After the build files, we need some HTML files.
- We start where a visitor would start, at `home.html`.
- IMPORTANT: The HTML files go in the `resources/templates` directory.
- Spring Boot knows to look for them in that location.
- The following listing shows our `home.html` file:
- ====
- [source,html]
- ----
- <!DOCTYPE html>
- <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
- <head>
- <title>Spring Security Example</title>
- </head>
- <body>
- <h1>Welcome!</h1>
- <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
- </body>
- </html>
- ----
- ====
- We also need a `hello.html` file, so that visitors to our web site can see the greeting we mention in the `home.html` file.
- The following listing shows the `home.html` file:
- ====
- [source,html]
- ----
- <!DOCTYPE html>
- <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
- xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
- <head>
- <title>Hello, World!</title>
- </head>
- <body>
- <h1>Hello, world!</h1>
- </body>
- </html>
- ----
- ====
- Once we have HTML pages for our visitors to see, we need to route them to the pages.
- We do that with a class that implements the `WebMvcConfigurer` (from the Spring framework).
- The following listing shows that class, which is called `MvcConfig`:
- ====
- [source,java]
- ----
- package com.example.securingweb;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
- @Configuration
- public class MvcConfig implements WebMvcConfigurer {
- public void addViewControllers(ViewControllerRegistry registry) {
- registry.addViewController("/home").setViewName("home");
- registry.addViewController("/").setViewName("home");
- registry.addViewController("/hello").setViewName("hello");
- }
- }
- ----
- ====
- Finally, we need an application class to give us an entry point for our program.
- We call it `SecuringWebApplication`, even though it is not yet secure.
- We cover how to secure it in the various recipes.
- The following application shows the `SecuringWebApplication` class:
- ====
- [source,java]
- ----
- package com.example.securingweb;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- @SpringBootApplication
- public class SecuringWebApplication {
- public static void main(String[] args) throws Throwable {
- SpringApplication.run(SecuringWebApplication.class, args);
- }
- }
- ----
- ====
- If we run this application now, we would see an unsecured web application.
- Now we can make it be a secure application.
- == Securing the Application
- To secure the simple web application presented in the <<security-cookbook-the-web-application,preceding section>>, we need to add the appropriate Spring Security dependencies to our build file (we show both Maven and Gradle).
- For Gradle, we need to add the following two lines to the `dependencies` block in our `build.gradle` file:
- ====
- [source,java]
- ----
- implementation 'org.springframework.boot:spring-boot-starter-security'
- implementation 'org.springframework.security:spring-security-test'
- ----
- ====
- The following listing shows the final `build.gradle` file:
- ====
- [source,java]
- ----
- plugins {
- id 'org.springframework.boot' version '2.2.0.RELEASE'
- id 'io.spring.dependency-management' version '1.0.8.RELEASE'
- id 'java'
- }
- group = 'com.example'
- version = '0.0.1-SNAPSHOT'
- sourceCompatibility = '1.8'
- repositories {
- mavenCentral()
- }
- dependencies {
- implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
- implementation 'org.springframework.boot:spring-boot-starter-web'
- implementation 'org.springframework.boot:spring-boot-starter-security'
- implementation 'org.springframework.security:spring-security-test'
- testImplementation('org.springframework.boot:spring-boot-starter-test') {
- exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
- }
- }
- test {
- useJUnitPlatform()
- }
- ----
- ====
- For Maven, we need to add the following two dependencies to the `dependencies` element in our `pom.xml` file:
- ====
- [source,xml]
- ----
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-test</artifactId>
- <scope>test</scope>
- </dependency>
- ----
- ====
- The following listing shows the final `pom.xml` file:
- ====
- [source,xml]
- ----
- <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.2.0.RELEASE</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
- <groupId>com.example</groupId>
- <artifactId>securing-web</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>securing-web</name>
- <description>Demo project for Spring Boot</description>
- <properties>
- <java.version>1.8</java.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-thymeleaf</artifactId>
- </dependency>
- <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.security</groupId>
- <artifactId>spring-security-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>org.junit.vintage</groupId>
- <artifactId>junit-vintage-engine</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
- </project>
- ----
- ====
- We also need a login page. The following HTML file serves that need:
- ====
- [source,html]
- ----
- <!DOCTYPE html>
- <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
- xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
- <head>
- <title>Spring Security Example </title>
- </head>
- <body>
- <div th:if="${param.error}">
- Invalid username and password.
- </div>
- <div th:if="${param.logout}">
- You have been logged out.
- </div>
- <form th:action="@{/login}" method="post">
- <div><label> User Name : <input type="text" name="username"/> </label></div>
- <div><label> Password: <input type="password" name="password"/> </label></div>
- <div><input type="submit" value="Sign In"/></div>
- </form>
- </body>
- </html>
- ----
- ====
- We also need to add a line to our `MvcConfig` class, as the following listing shows:
- ====
- [source,java]
- ----
- package com.example.securingweb;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
- @Configuration
- public class MvcConfig implements WebMvcConfigurer {
- public void addViewControllers(ViewControllerRegistry registry) {
- registry.addViewController("/home").setViewName("home");
- registry.addViewController("/").setViewName("home");
- registry.addViewController("/hello").setViewName("hello");
- registry.addViewController("/login").setViewName("login"); <1>
- }
- }
- ----
- <1> We need to add this line to make the `/login` path work.
- ====
- We also need a class to configure security for our web application.
- The following listing shows that class (called `WebSecurityConfig`):
- ====
- [source,java]
- ----
- package com.example.securingweb;
- 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.core.userdetails.User;
- import org.springframework.security.core.userdetails.UserDetails;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.provisioning.InMemoryUserDetailsManager;
- @Configuration
- @EnableWebSecurity
- public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- .authorizeRequests() <1>
- .antMatchers("/", "/home").permitAll() <2>
- .anyRequest().authenticated() <3>
- .and()
- .formLogin() <4>
- .loginPage("/login") <5>
- .permitAll()
- .and()
- .logout() <6>
- .permitAll();
- }
- @Bean
- @Override
- public UserDetailsService userDetailsService() {
- UserDetails user = <7>
- User.withDefaultPasswordEncoder()
- .username("user") <8>
- .password("password") <9>
- .roles("USER") <10>
- .build(); <11>
- return new InMemoryUserDetailsManager(user);
- }
- }
- ----
- <1> Turn on security by authorizing request.
- <2> Let anyone see the default and `home` paths.
- <3> Require that any request be authenticated. (This is where we apply security.)
- <4> Allow a login form.
- <5> Allow that form from the `/login` path.
- <6> Let anyone see the logout page.
- <7> Define a user object.
- <8> The user's user name is `user`.
- <9> The user's user name is `password`.
- <10> The user's role is `USER`.
- <11> Build the user object.
- ====
- WARNING: _NEVER_ put user names and passwords in code for a real application.
- It is tolerable for demonstrations and samples, but it is very poor practice for real applications.
- The `WebSecurityConfig` class has two key parts: A `configure` method (which overrides the `configure` method in `WebSecurityConfigurerAdapter`) and a `UserDetailsService` bean.
- The `configure` method has a chain of methods that define the security for the paths in our application.
- In essence, the preceding configuration says, "`Let anyone see the login and logout pages. Make everyone authenticate (log in) to see anything else.`"
- We also define the one and only user who can view our web application.
- Normally, we would get user details from a database or an LDAP or OAuth server (or from some other source - many options exist).
- We created this simple arrangement to show the basic outline of what happens.
|