JAVA-3506: moved spring-5-security inside spring-security-modules

This commit is contained in:
sampadawagde
2020-12-25 22:42:29 +05:30
parent c8a0a241b9
commit 98e44b72b1
64 changed files with 1 additions and 1 deletions

View File

@@ -0,0 +1,14 @@
## Spring 5 Security
This module contains articles about Spring Security 5
## Relevant articles:
- [Extra Login Fields with Spring Security](https://www.baeldung.com/spring-security-extra-login-fields)
- [A Custom Spring SecurityConfigurer](https://www.baeldung.com/spring-security-custom-configurer)
- [New Password Storage In Spring Security 5](https://www.baeldung.com/spring-security-5-password-storage)
- [Default Password Encoder in Spring Security 5](https://www.baeldung.com/spring-security-5-default-password-encoder)
- [Guide to the AuthenticationManagerResolver in Spring Security](https://www.baeldung.com/spring-security-authenticationmanagerresolver)
- [Disable Security for a Profile in Spring Boot](https://www.baeldung.com/spring-security-disable-profile)
- [Manual Logout With Spring Security](https://www.baeldung.com/spring-security-manual-logout)
- [How to Disable Spring Security Logout Redirects](https://www.baeldung.com/spring-security-disable-logout-redirects)

View File

@@ -0,0 +1,64 @@
<?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>
<artifactId>spring-5-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-5-security</name>
<packaging>jar</packaging>
<description>spring 5 security sample project</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-2</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</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-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<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>
<configuration>
<mainClass>com.baeldung.passwordstorage.PasswordStorageApplication</mainClass>
<layout>JAR</layout>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,11 @@
package com.baeldung.authresolver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AuthResolverApplication {
public static void main(String[] args) {
SpringApplication.run(AuthResolverApplication.class, args);
}
}

View File

@@ -0,0 +1,18 @@
package com.baeldung.authresolver;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AuthResolverController {
@GetMapping("/customer/welcome")
public String sayWelcomeToCustomer(Authentication authentication) {
return String.format("Welcome to our site, %s!", authentication.getPrincipal());
}
@GetMapping("/employee/welcome")
public String sayWelcomeToEmployee(Authentication authentication) {
return String.format("Welcome to our company, %s!", authentication.getPrincipal());
}
}

View File

@@ -0,0 +1,96 @@
package com.baeldung.authresolver;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationConverter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@Configuration
public class CustomWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
public AuthenticationConverter authenticationConverter() {
return new BasicAuthenticationConverter();
}
public AuthenticationManagerResolver<HttpServletRequest> resolver() {
return request -> {
if (request
.getPathInfo()
.startsWith("/employee")) {
return employeesAuthenticationManager();
}
return customersAuthenticationManager();
};
}
public AuthenticationManager customersAuthenticationManager() {
return authentication -> {
if (isCustomer(authentication)) {
return new UsernamePasswordAuthenticationToken(
authentication.getPrincipal(),
authentication.getCredentials(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
);
}
throw new UsernameNotFoundException(authentication
.getPrincipal()
.toString());
};
}
private boolean isCustomer(Authentication authentication) {
return (authentication
.getPrincipal()
.toString()
.startsWith("customer"));
}
private boolean isEmployee(Authentication authentication) {
return (authentication
.getPrincipal()
.toString()
.startsWith("employee"));
}
private AuthenticationFilter authenticationFilter() {
AuthenticationFilter filter = new AuthenticationFilter(
resolver(), authenticationConverter());
filter.setSuccessHandler((request, response, auth) -> {});
return filter;
}
private AuthenticationManager employeesAuthenticationManager() {
return authentication -> {
if (isEmployee(authentication)) {
return new UsernamePasswordAuthenticationToken(
authentication.getPrincipal(),
authentication.getCredentials(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
);
}
throw new UsernameNotFoundException(authentication
.getPrincipal()
.toString());
};
}
@Override
protected void configure(HttpSecurity http) {
http.addFilterBefore(
authenticationFilter(),
BasicAuthenticationFilter.class
);
}
}

View File

@@ -0,0 +1,32 @@
package com.baeldung.dsl;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
public class ClientErrorLoggingConfigurer extends AbstractHttpConfigurer<ClientErrorLoggingConfigurer, HttpSecurity> {
private List<HttpStatus> errorCodes;
public ClientErrorLoggingConfigurer(List<HttpStatus> errorCodes) {
this.errorCodes = errorCodes;
}
public ClientErrorLoggingConfigurer() {
}
@Override
public void init(HttpSecurity http) throws Exception {
// initialization code
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(new ClientErrorLoggingFilter(errorCodes), FilterSecurityInterceptor.class);
}
}

View File

@@ -0,0 +1,57 @@
package com.baeldung.dsl;
import java.io.IOException;
import java.util.List;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
public class ClientErrorLoggingFilter extends GenericFilterBean {
private static final Logger logger = LogManager.getLogger(ClientErrorLoggingFilter.class);
private List<HttpStatus> errorCodes;
public ClientErrorLoggingFilter(List<HttpStatus> errorCodes) {
this.errorCodes = errorCodes;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
if (auth == null) {
chain.doFilter(request, response);
return;
}
int status = ((HttpServletResponse) response).getStatus();
if (status < 400 || status >= 500) {
chain.doFilter(request, response);
return;
}
if (errorCodes == null) {
logger.debug("User " + auth.getName() + " encountered error " + status);
} else {
if (errorCodes.stream()
.anyMatch(s -> s.value() == status)) {
logger.debug("User " + auth.getName() + " encountered error " + status);
}
}
chain.doFilter(request, response);
}
}

View File

@@ -0,0 +1,13 @@
package com.baeldung.dsl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CustomConfigurerApplication {
public static void main(String[] args) {
SpringApplication.run(CustomConfigurerApplication.class, args);
}
}

View File

@@ -0,0 +1,14 @@
package com.baeldung.dsl;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@GetMapping("/admin")
public String getAdminPage() {
return "Hello Admin";
}
}

View File

@@ -0,0 +1,50 @@
package com.baeldung.dsl;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin*")
.hasAnyRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.formLogin()
.and()
.apply(clientErrorLogging());
}
@Bean
public ClientErrorLoggingConfigurer clientErrorLogging() {
return new ClientErrorLoggingConfigurer();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("user1")
.password(passwordEncoder().encode("user"))
.roles("USER")
.and()
.withUser("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,14 @@
package com.baeldung.inmemory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class InMemoryAuthApplication {
public static void main(String[] args) {
SpringApplication.run(InMemoryAuthApplication.class, args);
}
}

View File

@@ -0,0 +1,21 @@
package com.baeldung.inmemory;
import java.util.Arrays;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class InMemoryAuthController {
@GetMapping("/public/hello")
public List<String> publicHello() {
return Arrays.asList("Hello", "World", "from", "Public");
}
@GetMapping("/private/hello")
public List<String> privateHello() {
return Arrays.asList("Hello", "World", "from", "Private");
}
}

View File

@@ -0,0 +1,34 @@
package com.baeldung.inmemory;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class InMemoryAuthWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth.inMemoryAuthentication()
.passwordEncoder(encoder)
.withUser("spring")
.password(encoder.encode("secret"))
.roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/private/**")
.authenticated()
.antMatchers("/public/**")
.permitAll()
.and()
.httpBasic();
}
}

View File

@@ -0,0 +1,29 @@
package com.baeldung.inmemory;
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.WebSecurityConfigurerAdapter;
//@Configuration
public class InMemoryNoOpAuthWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("spring")
.password("{noop}secret")
.roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/private/**")
.authenticated()
.antMatchers("/public/**")
.permitAll()
.and()
.httpBasic();
}
}

View File

@@ -0,0 +1,50 @@
package com.baeldung.loginextrafieldscustom;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain";
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: "
+ request.getMethod());
}
CustomAuthenticationToken authRequest = getAuthRequest(request);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
private CustomAuthenticationToken getAuthRequest(HttpServletRequest request) {
String username = obtainUsername(request);
String password = obtainPassword(request);
String domain = obtainDomain(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
if (domain == null) {
domain = "";
}
return new CustomAuthenticationToken(username, password, domain);
}
private String obtainDomain(HttpServletRequest request) {
return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY);
}
}

View File

@@ -0,0 +1,28 @@
package com.baeldung.loginextrafieldscustom;
import java.util.Collection;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken {
private String domain;
public CustomAuthenticationToken(Object principal, Object credentials, String domain) {
super(principal, credentials);
this.domain = domain;
super.setAuthenticated(false);
}
public CustomAuthenticationToken(Object principal, Object credentials, String domain,
Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
this.domain = domain;
super.setAuthenticated(true); // must use super, as we override
}
public String getDomain() {
return this.domain;
}
}

View File

@@ -0,0 +1,92 @@
package com.baeldung.loginextrafieldscustom;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Assert;
public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
/**
* The plaintext password used to perform
* PasswordEncoder#matches(CharSequence, String)} on when the user is
* not found to avoid SEC-2056.
*/
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
private PasswordEncoder passwordEncoder;
private CustomUserDetailsService userDetailsService;
/**
* The password used to perform
* {@link PasswordEncoder#matches(CharSequence, String)} on when the user is
* not found to avoid SEC-2056. This is necessary, because some
* {@link PasswordEncoder} implementations will short circuit if the password is not
* in a valid format.
*/
private String userNotFoundEncodedPassword;
public CustomUserDetailsAuthenticationProvider(PasswordEncoder passwordEncoder, CustomUserDetailsService userDetailsService) {
this.passwordEncoder = passwordEncoder;
this.userDetailsService = userDetailsService;
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(
messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
String presentedPassword = authentication.getCredentials()
.toString();
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(
messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
@Override
protected void doAfterPropertiesSet() throws Exception {
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
}
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
CustomAuthenticationToken auth = (CustomAuthenticationToken) authentication;
UserDetails loadedUser;
try {
loadedUser = this.userDetailsService.loadUserByUsernameAndDomain(auth.getPrincipal()
.toString(), auth.getDomain());
} catch (UsernameNotFoundException notFound) {
if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials()
.toString();
passwordEncoder.matches(presentedPassword, userNotFoundEncodedPassword);
}
throw notFound;
} catch (Exception repositoryProblem) {
throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
throw new InternalAuthenticationServiceException("UserDetailsService returned null, "
+ "which is an interface contract violation");
}
return loadedUser;
}
}

View File

@@ -0,0 +1,10 @@
package com.baeldung.loginextrafieldscustom;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public interface CustomUserDetailsService {
UserDetails loadUserByUsernameAndDomain(String username, String domain) throws UsernameNotFoundException;
}

View File

@@ -0,0 +1,30 @@
package com.baeldung.loginextrafieldscustom;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service("userDetailsService")
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
private UserRepository userRepository;
public CustomUserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsernameAndDomain(String username, String domain) throws UsernameNotFoundException {
if (StringUtils.isAnyBlank(username, domain)) {
throw new UsernameNotFoundException("Username and domain must be provided");
}
User user = userRepository.findUser(username, domain);
if (user == null) {
throw new UsernameNotFoundException(
String.format("Username not found for domain, username=%s, domain=%s",
username, domain));
}
return user;
}
}

View File

@@ -0,0 +1,33 @@
package com.baeldung.loginextrafieldscustom;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;
@Repository("userRepository")
public class CustomUserRepository implements UserRepository {
private PasswordEncoder passwordEncoder;
public CustomUserRepository(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
public User findUser(String username, String domain) {
if (StringUtils.isAnyBlank(username, domain)) {
return null;
} else {
Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
User user = new User(username, domain,
passwordEncoder.encode("secret"), true,
true, true, true, authorities);
return user;
}
}
}

View File

@@ -0,0 +1,13 @@
package com.baeldung.loginextrafieldscustom;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ExtraLoginFieldsApplication {
public static void main(String[] args) {
SpringApplication.run(ExtraLoginFieldsApplication.class, args);
}
}

View File

@@ -0,0 +1,15 @@
package com.baeldung.loginextrafieldscustom;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class PasswordEncoderConfiguration {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,63 @@
package com.baeldung.loginextrafieldscustom;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.security.authentication.AuthenticationProvider;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@EnableWebSecurity
@PropertySource("classpath:/application-extrafields.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/user/**").authenticated()
.and()
.formLogin().loginPage("/login")
.and()
.logout()
.logoutUrl("/logout");
}
public CustomAuthenticationFilter authenticationFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationFailureHandler(failureHandler());
return filter;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
public AuthenticationProvider authProvider() {
CustomUserDetailsAuthenticationProvider provider
= new CustomUserDetailsAuthenticationProvider(passwordEncoder, userDetailsService);
return provider;
}
public SimpleUrlAuthenticationFailureHandler failureHandler() {
return new SimpleUrlAuthenticationFailureHandler("/login?error=true");
}
}

View File

@@ -0,0 +1,23 @@
package com.baeldung.loginextrafieldscustom;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
public class User extends org.springframework.security.core.userdetails.User {
private static final long serialVersionUID = 1L;
private String domain;
public User(String username, String domain, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.domain = domain;
}
public String getDomain() {
return domain;
}
}

View File

@@ -0,0 +1,7 @@
package com.baeldung.loginextrafieldscustom;
public interface UserRepository {
public User findUser(String username, String domain);
}

View File

@@ -0,0 +1,51 @@
package com.baeldung.loginextrafieldscustom;
import java.util.Optional;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class WebController {
@RequestMapping("/")
public String root() {
return "redirect:/index";
}
@RequestMapping("/index")
public String index(Model model) {
getDomain().ifPresent(d -> {
model.addAttribute("domain", d);
});
return "index";
}
@RequestMapping("/user/index")
public String userIndex(Model model) {
getDomain().ifPresent(d -> {
model.addAttribute("domain", d);
});
return "user/index";
}
@RequestMapping("/login")
public String login() {
return "login";
}
private Optional<String> getDomain() {
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
String domain = null;
if (auth != null && !auth.getClass().equals(AnonymousAuthenticationToken.class)) {
User user = (User) auth.getPrincipal();
domain = user.getDomain();
}
return Optional.ofNullable(domain);
}
}

View File

@@ -0,0 +1,13 @@
package com.baeldung.loginextrafieldssimple;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ExtraLoginFieldsApplication {
public static void main(String[] args) {
SpringApplication.run(ExtraLoginFieldsApplication.class, args);
}
}

View File

@@ -0,0 +1,15 @@
package com.baeldung.loginextrafieldssimple;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class PasswordEncoderConfiguration {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,66 @@
package com.baeldung.loginextrafieldssimple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@EnableWebSecurity
@PropertySource("classpath:/application-extrafields.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/user/**").authenticated()
.and()
.formLogin().loginPage("/login")
.and()
.logout()
.logoutUrl("/logout");
}
public SimpleAuthenticationFilter authenticationFilter() throws Exception {
SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationFailureHandler(failureHandler());
return filter;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
public AuthenticationProvider authProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder);
return provider;
}
public SimpleUrlAuthenticationFailureHandler failureHandler() {
return new SimpleUrlAuthenticationFailureHandler("/login?error=true");
}
}

View File

@@ -0,0 +1,54 @@
package com.baeldung.loginextrafieldssimple;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class SimpleAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain";
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if (!request.getMethod()
.equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
UsernamePasswordAuthenticationToken authRequest = getAuthRequest(request);
setDetails(request, authRequest);
return this.getAuthenticationManager()
.authenticate(authRequest);
}
private UsernamePasswordAuthenticationToken getAuthRequest(HttpServletRequest request) {
String username = obtainUsername(request);
String password = obtainPassword(request);
String domain = obtainDomain(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
if (domain == null) {
domain = "";
}
String usernameDomain = String.format("%s%s%s", username.trim(),
String.valueOf(Character.LINE_SEPARATOR), domain);
return new UsernamePasswordAuthenticationToken(usernameDomain, password);
}
private String obtainDomain(HttpServletRequest request) {
return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY);
}
}

View File

@@ -0,0 +1,32 @@
package com.baeldung.loginextrafieldssimple;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service("userDetailsService")
public class SimpleUserDetailsService implements UserDetailsService {
private UserRepository userRepository;
public SimpleUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String[] usernameAndDomain = StringUtils.split(username, String.valueOf(Character.LINE_SEPARATOR));
if (usernameAndDomain == null || usernameAndDomain.length != 2) {
throw new UsernameNotFoundException("Username and domain must be provided");
}
User user = userRepository.findUser(usernameAndDomain[0], usernameAndDomain[1]);
if (user == null) {
throw new UsernameNotFoundException(
String.format("Username not found for domain, username=%s, domain=%s",
usernameAndDomain[0], usernameAndDomain[1]));
}
return user;
}
}

View File

@@ -0,0 +1,33 @@
package com.baeldung.loginextrafieldssimple;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;
@Repository("userRepository")
public class SimpleUserRepository implements UserRepository {
private PasswordEncoder passwordEncoder;
public SimpleUserRepository(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
public User findUser(String username, String domain) {
if (StringUtils.isAnyBlank(username, domain)) {
return null;
} else {
Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
User user = new User(username, domain,
passwordEncoder.encode("secret"), true,
true, true, true, authorities);
return user;
}
}
}

View File

@@ -0,0 +1,21 @@
package com.baeldung.loginextrafieldssimple;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
public class User extends org.springframework.security.core.userdetails.User {
private String domain;
public User(String username, String domain, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.domain = domain;
}
public String getDomain() {
return domain;
}
}

View File

@@ -0,0 +1,7 @@
package com.baeldung.loginextrafieldssimple;
public interface UserRepository {
public User findUser(String username, String domain);
}

View File

@@ -0,0 +1,51 @@
package com.baeldung.loginextrafieldssimple;
import java.util.Optional;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class WebController {
@RequestMapping("/")
public String root() {
return "redirect:/index";
}
@RequestMapping("/index")
public String index(Model model) {
getDomain().ifPresent(d -> {
model.addAttribute("domain", d);
});
return "index";
}
@RequestMapping("/user/index")
public String userIndex(Model model) {
getDomain().ifPresent(d -> {
model.addAttribute("domain", d);
});
return "user/index";
}
@RequestMapping("/login")
public String login() {
return "login";
}
private Optional<String> getDomain() {
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
String domain = null;
if (auth != null && !auth.getClass().equals(AnonymousAuthenticationToken.class)) {
User user = (User) auth.getPrincipal();
domain = user.getDomain();
}
return Optional.ofNullable(domain);
}
}

View File

@@ -0,0 +1,13 @@
package com.baeldung.logoutredirects;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class LogoutApplication {
public static void main(String[] args) {
SpringApplication.run(LogoutApplication.class, args);
}
}

View File

@@ -0,0 +1,27 @@
package com.baeldung.logoutredirects.securityconfig;
import javax.servlet.http.HttpServletResponse;
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;
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(authz -> authz.mvcMatchers("/login")
.permitAll()
.anyRequest()
.authenticated())
.logout(logout -> logout.permitAll()
.logoutSuccessHandler((request, response, authentication) -> {
response.setStatus(HttpServletResponse.SC_OK);
}));
}
}

View File

@@ -0,0 +1,11 @@
package com.baeldung.manuallogout;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ManualLogoutApplication {
public static void main(String[] args) {
SpringApplication.run(ManualLogoutApplication.class, args);
}
}

View File

@@ -0,0 +1,76 @@
package com.baeldung.manuallogout;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.logout.HeaderWriterLogoutHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter;
import javax.servlet.http.Cookie;
import static org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter.Directive.*;
@Configuration
@EnableWebSecurity
public class SimpleSecurityConfiguration {
@Order(3)
@Configuration
public static class DefaultLogoutConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/basic/**")
.authorizeRequests(authz -> authz.anyRequest().permitAll())
.logout(logout -> logout
.logoutUrl("/basic/basiclogout")
);
}
}
@Order(2)
@Configuration
public static class AllCookieClearingLogoutConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/cookies/**")
.authorizeRequests(authz -> authz.anyRequest().permitAll())
.logout(logout -> logout
.logoutUrl("/cookies/cookielogout")
.addLogoutHandler(new SecurityContextLogoutHandler())
.addLogoutHandler((request, response, auth) -> {
for (Cookie cookie : request.getCookies()) {
String cookieName = cookie.getName();
Cookie cookieToDelete = new Cookie(cookieName, null);
cookieToDelete.setMaxAge(0);
response.addCookie(cookieToDelete);
}
})
);
}
}
@Order(1)
@Configuration
public static class ClearSiteDataHeaderLogoutConfiguration extends WebSecurityConfigurerAdapter {
private static final ClearSiteDataHeaderWriter.Directive[] SOURCE =
{CACHE, COOKIES, STORAGE, EXECUTION_CONTEXTS};
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/csd/**")
.authorizeRequests(authz -> authz.anyRequest().permitAll())
.logout(logout -> logout
.logoutUrl("/csd/csdlogout")
.addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(SOURCE)))
);
}
}
}

View File

@@ -0,0 +1,35 @@
package com.baeldung.passwordstorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class BaeldungPasswordEncoderSetup {
private final static Logger LOG = LoggerFactory.getLogger(BaeldungPasswordEncoderSetup.class);
@Bean
public ApplicationListener<AuthenticationSuccessEvent> authenticationSuccessListener(final PasswordEncoder encoder) {
return (AuthenticationSuccessEvent event) -> {
final Authentication auth = event.getAuthentication();
if (auth instanceof UsernamePasswordAuthenticationToken && auth.getCredentials() != null) {
final CharSequence clearTextPass = (CharSequence) auth.getCredentials(); // 1
final String newPasswordHash = encoder.encode(clearTextPass); // 2
LOG.info("New password hash {} for user {}", newPasswordHash, auth.getName());
((UsernamePasswordAuthenticationToken) auth).eraseCredentials(); // 3
}
};
}
}

View File

@@ -0,0 +1,13 @@
package com.baeldung.passwordstorage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class PasswordStorageApplication {
public static void main(String[] args) {
SpringApplication.run(PasswordStorageApplication.class, args);
}
}

View File

@@ -0,0 +1,55 @@
package com.baeldung.passwordstorage;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class PasswordStorageWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.eraseCredentials(false) // 4
.userDetailsService(getUserDefaultDetailsService())
.passwordEncoder(passwordEncoder());
}
@Bean
public UserDetailsService getUserDefaultDetailsService() {
return new InMemoryUserDetailsManager(User
.withUsername("baeldung")
.password("{noop}SpringSecurity5")
.authorities(Collections.emptyList())
.build());
}
@Bean
public PasswordEncoder passwordEncoder() {
// set up the list of supported encoders and their prefixes
PasswordEncoder defaultEncoder = new StandardPasswordEncoder();
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("noop", NoOpPasswordEncoder.getInstance());
DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder("bcrypt", encoders);
passwordEncoder.setDefaultPasswordEncoderForMatches(defaultEncoder);
return passwordEncoder;
}
}

View File

@@ -0,0 +1,14 @@
package com.baeldung.securityprofile;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@SpringBootApplication
@EnableWebSecurity
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,17 @@
package com.baeldung.securityprofile;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@Profile("test")
public class ApplicationNoSecurity extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/**");
}
}

View File

@@ -0,0 +1,16 @@
package com.baeldung.securityprofile;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@Profile("prod")
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}

View File

@@ -0,0 +1,16 @@
package com.baeldung.securityprofile;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
@RestController
public class EmployeeController {
@GetMapping("/employees")
public List<String> getEmployees() {
return Collections.singletonList("Adam Johnson");
}
}

View File

@@ -0,0 +1 @@
spring.thymeleaf.prefix = classpath:/templatesextrafields/

View File

@@ -0,0 +1,7 @@
server.port=8081
logging.level.root=INFO
logging.level.com.baeldung.dsl.ClientErrorLoggingFilter=DEBUG
logging.level.org.springframework.security=DEBUG

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,8 @@
p.error {
font-weight: bold;
color: red;
}
div.logout {
margin-right: 2em;;
}

View File

@@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Home</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
Home
Welcome!
</body>
</html>

View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" lang="en">
<head>
<title>Spring Security with Extra Fields</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="http://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<div class="container">
<div class="logout float-right" th:fragment="logout" sec:authorize="isAuthenticated()">
<p>Logged in: <span sec:authentication="name"></span> | <span th:text="${domain}">Some Domain</span>
</p>
<div>
<form action="#" th:action="@{/logout}" method="post">
<button class="btn btn-sm btn-primary btn-block" type="submit">Logout</button>
</form>
</div>
</div>
<h2>Hello Spring Security</h2>
<p>This is an unsecured page, but you can access the secured pages after authenticating.</p>
<ul>
<li>Go to the <a href="/user/index" th:href="@{/user/index}">secured pages</a></li>
</ul>
</div>
</body>
</html>

View File

@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<title>Login page</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="http://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<div class="container">
<form class="form-signin" th:action="@{/login}" method="post">
<h2 class="form-signin-heading">Please sign in</h2>
<p>Example: user / domain / password</p>
<p th:if="${param.error}" class="error">Invalid user, password, or domain</p>
<p>
<label for="username" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus/>
</p>
<p>
<label for="domain" class="sr-only">Domain</label>
<input type="text" id="domain" name="domain" class="form-control" placeholder="Domain" required autofocus/>
</p>
<p>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required autofocus/>
</p>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button><br/>
<p><a href="/index" th:href="@{/index}">Back to home page</a></p>
</form>
</div>
</body>
</html>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<title>Secured Page</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="http://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<div class="container">
<div th:replace="index::logout"></div>
<h2>This is a secured page!</h2>
<p><a href="/index" th:href="@{/index}">Back to home page</a></p>
</div>
</body>
</html>

View File

@@ -0,0 +1,17 @@
package com.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.dsl.CustomConfigurerApplication;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CustomConfigurerApplication.class)
public class SpringContextTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}

View File

@@ -0,0 +1,82 @@
package com.baeldung.authresolver;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.Base64Utils;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AuthResolverApplication.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class AuthResolverIntegrationTest {
@Autowired
private FilterChainProxy springSecurityFilterChain;
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(springSecurity(springSecurityFilterChain))
.build();
}
@Test
public void givenCustomerCredential_whenWelcomeCustomer_thenExpectOk() throws Exception {
this.mockMvc
.perform(get("/customer/welcome")
.header(
"Authorization", String.format("Basic %s", Base64Utils.encodeToString("customer1:pass1".getBytes()))
)
)
.andExpect(status().is2xxSuccessful());
}
@Test
public void givenEmployeeCredential_whenWelcomeCustomer_thenExpect401Status() throws Exception {
this.mockMvc
.perform(get("/customer/welcome")
.header(
"Authorization", "Basic " + Base64Utils.encodeToString("employee1:pass1".getBytes()))
)
.andExpect(status().isUnauthorized());
}
@Test
public void givenEmployeeCredential_whenWelcomeEmployee_thenExpectOk() throws Exception {
this.mockMvc
.perform(get("/employee/welcome")
.header(
"Authorization", "Basic " + Base64Utils.encodeToString("employee1:pass1".getBytes()))
)
.andExpect(status().is2xxSuccessful());
}
@Test
public void givenCustomerCredential_whenWelcomeEmployee_thenExpect401Status() throws Exception {
this.mockMvc
.perform(get("/employee/welcome")
.header(
"Authorization", "Basic " + Base64Utils.encodeToString("customer1:pass1".getBytes()))
)
.andExpect(status().isUnauthorized());
}
}

View File

@@ -0,0 +1,48 @@
package com.baeldung.inmemory;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = InMemoryAuthApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class InMemoryAuthControllerIntegrationTest {
@Autowired
private TestRestTemplate template;
@Test
public void givenRequestOnPublicService_shouldSucceedWith200() throws Exception {
ResponseEntity<String> result = template.getForEntity("/public/hello", String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
}
@Test
public void givenRequestOnPrivateService_shouldFailWith401() throws Exception {
ResponseEntity<String> result = template.getForEntity("/private/hello", String.class);
assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode());
}
@Test
public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
ResponseEntity<String> result = template.withBasicAuth("spring", "secret")
.getForEntity("/private/hello", String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
}
@Test
public void givenInvalidAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
ResponseEntity<String> result = template.withBasicAuth("spring", "wrong")
.getForEntity("/private/hello", String.class);
assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode());
}
}

View File

@@ -0,0 +1,46 @@
package com.baeldung.loginextrafields;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
public abstract class AbstractExtraLoginFieldsIntegrationTest {
@Autowired
private FilterChainProxy springSecurityFilterChain;
@Autowired
private WebApplicationContext wac;
protected MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.apply(springSecurity(springSecurityFilterChain))
.build();
}
@Test
public void givenRootPathAccess_thenRedirectToIndex() throws Exception {
this.mockMvc.perform(get("/"))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrlPattern("/index*"));
}
@Test
public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception {
this.mockMvc.perform(get("/user/index"))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrlPattern("**/login"));
}
}

View File

@@ -0,0 +1,72 @@
package com.baeldung.loginextrafields;
import static org.junit.Assert.assertEquals;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import com.baeldung.loginextrafieldscustom.ExtraLoginFieldsApplication;
import com.baeldung.loginextrafieldscustom.User;
@RunWith(SpringRunner.class)
@SpringJUnitWebConfig
@SpringBootTest(classes = ExtraLoginFieldsApplication.class)
public class LoginFieldsFullIntegrationTest extends AbstractExtraLoginFieldsIntegrationTest {
@Test
public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception {
MockHttpServletRequestBuilder securedResourceAccess = get("/user/index");
MvcResult unauthenticatedResult = mockMvc.perform(securedResourceAccess)
.andExpect(status().is3xxRedirection())
.andReturn();
MockHttpSession session = (MockHttpSession) unauthenticatedResult.getRequest()
.getSession();
String loginUrl = unauthenticatedResult.getResponse()
.getRedirectedUrl();
User user = getUser();
mockMvc.perform(post(loginUrl)
.param("username", user.getUsername())
.param("password", user.getPassword())
.param("domain", user.getDomain())
.session(session)
.with(csrf()))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrlPattern("**/user/index"))
.andReturn();
mockMvc.perform(securedResourceAccess.session(session))
.andExpect(status().isOk());
SecurityContext securityContext
= (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
Authentication auth = securityContext.getAuthentication();
assertEquals(((User)auth.getPrincipal()).getDomain(), user.getDomain());
}
private User getUser() {
Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
return new User("myusername", "mydomain", "secret", true, true, true, true, authorities);
}
}

View File

@@ -0,0 +1,72 @@
package com.baeldung.loginextrafields;
import static org.junit.Assert.assertEquals;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import com.baeldung.loginextrafieldssimple.ExtraLoginFieldsApplication;
import com.baeldung.loginextrafieldssimple.User;
@RunWith(SpringRunner.class)
@SpringJUnitWebConfig
@SpringBootTest(classes = ExtraLoginFieldsApplication.class)
public class LoginFieldsSimpleIntegrationTest extends AbstractExtraLoginFieldsIntegrationTest {
@Test
public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception {
MockHttpServletRequestBuilder securedResourceAccess = get("/user/index");
MvcResult unauthenticatedResult = mockMvc.perform(securedResourceAccess)
.andExpect(status().is3xxRedirection())
.andReturn();
MockHttpSession session = (MockHttpSession) unauthenticatedResult.getRequest()
.getSession();
String loginUrl = unauthenticatedResult.getResponse()
.getRedirectedUrl();
User user = getUser();
mockMvc.perform(post(loginUrl)
.param("username", user.getUsername())
.param("password", user.getPassword())
.param("domain", user.getDomain())
.session(session)
.with(csrf()))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrlPattern("**/user/index"))
.andReturn();
mockMvc.perform(securedResourceAccess.session(session))
.andExpect(status().isOk());
SecurityContext securityContext
= (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
Authentication auth = securityContext.getAuthentication();
assertEquals(((User)auth.getPrincipal()).getDomain(), user.getDomain());
}
private User getUser() {
Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
return new User("myusername", "mydomain", "secret", true, true, true, true, authorities);
}
}

View File

@@ -0,0 +1,34 @@
package com.baeldung.logoutredirects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringRunner.class)
@WebMvcTest()
public class LogoutApplicationUnitTest {
@Autowired
private MockMvc mockMvc;
@WithMockUser(value = "spring")
@Test
public void whenLogout_thenDisableRedirect() throws Exception {
this.mockMvc.perform(post("/logout").with(csrf()))
.andExpect(status().isOk())
.andExpect(jsonPath("$").doesNotExist())
.andExpect(unauthenticated())
.andReturn();
}
}

View File

@@ -0,0 +1,79 @@
package com.baeldung.manuallogout;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import static org.junit.Assert.assertNull;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringRunner.class)
@WebMvcTest()
public class ManualLogoutIntegrationTest {
private static final String CLEAR_SITE_DATA_HEADER = "Clear-Site-Data";
public static final int EXPIRY = 60 * 10;
public static final String COOKIE_NAME = "customerName";
public static final String COOKIE_VALUE = "myName";
public static final String ATTRIBUTE_NAME = "att";
public static final String ATTRIBUTE_VALUE = "attvalue";
@Autowired
private MockMvc mockMvc;
@WithMockUser(value = "spring")
@Test
public void givenLoggedUserWhenUserLogoutThenSessionClearedAndNecessaryCookieCleared() throws Exception {
this.mockMvc.perform(post("/basic/basiclogout").secure(true).with(csrf()))
.andExpect(status().is3xxRedirection())
.andExpect(unauthenticated())
.andReturn();
}
@WithMockUser(value = "spring")
@Test
public void givenLoggedUserWhenUserLogoutThenSessionClearedAndAllCookiesCleared() throws Exception {
MockHttpSession session = new MockHttpSession();
session.setAttribute(ATTRIBUTE_NAME, ATTRIBUTE_VALUE);
Cookie randomCookie = new Cookie(COOKIE_NAME, COOKIE_VALUE);
randomCookie.setMaxAge(EXPIRY); // 10 minutes
MockHttpServletRequest requestStateAfterLogout = this.mockMvc.perform(post("/cookies/cookielogout").secure(true).with(csrf()).session(session).cookie(randomCookie))
.andExpect(status().is3xxRedirection())
.andExpect(unauthenticated())
.andExpect(cookie().maxAge(COOKIE_NAME, 0))
.andReturn()
.getRequest();
HttpSession sessionStateAfterLogout = requestStateAfterLogout.getSession();
assertNull(sessionStateAfterLogout.getAttribute(ATTRIBUTE_NAME));
}
@WithMockUser(value = "spring")
@Test
public void givenLoggedUserWhenUserLogoutThenClearDataSiteHeaderPresent() throws Exception {
this.mockMvc.perform(post("/csd/csdlogout").secure(true).with(csrf()))
.andDo(print())
.andExpect(status().is3xxRedirection())
.andExpect(header().exists(CLEAR_SITE_DATA_HEADER))
.andReturn();
}
}

View File

@@ -0,0 +1,28 @@
package com.baeldung.securityprofile;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@WebMvcTest(value = EmployeeController.class)
@ActiveProfiles("test")
public class EmployeeControllerNoSecurityUnitTest {
@Autowired
private MockMvc mockMvc;
@Test
public void whenSecurityDisabled_shouldBeOk() throws Exception {
this.mockMvc.perform(get("/employees"))
.andExpect(status().isOk());
}
}

View File

@@ -0,0 +1,28 @@
package com.baeldung.securityprofile;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@WebMvcTest(value = EmployeeController.class)
@ActiveProfiles("prod")
public class EmployeeControllerUnitTest {
@Autowired
private MockMvc mockMvc;
@Test
public void whenSecurityEnabled_shouldBeForbidden() throws Exception {
this.mockMvc.perform(get("/employees"))
.andExpect(status().isForbidden());
}
}