I'm using Spring Boot with annotations and Spring Security.
I need to implement two different kind of authentication:
ProviderApiAuthenticationProvider only for "/providerrpc" and "/api/(system|provider|drm)/"
TestAuthFilter (Custom authentificator, now empty), only for "/test/**"
In current configuration on both URL's app requests httpBasic authentification and TestAuthFilter::doFilter() also called on both URL's.
So, whats wrong?
WebSecurityConfig.java:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final ProviderApiAuthenticationProvider providerApiAuthenticationProvider;
private final TestAuthFilter testAuthFilter;
#Autowired
public WebSecurityConfig(TestAuthFilter testAuthFilter, ProviderApiAuthenticationProvider providerApiAuthenticationProvider) {
this.testAuthFilter = testAuthFilter;
this.providerApiAuthenticationProvider = providerApiAuthenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(providerApiAuthenticationProvider);
}
#SuppressWarnings("SpellCheckingInspection")
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authenticationProvider(providerApiAuthenticationProvider)
.authorizeRequests()
.regexMatchers(
"^/providerrpc/",
"^/api/(system|provider|drm)/"
)
.hasAuthority(Role.ROLE_PROVIDER_API.getAuthority())
.and()
.httpBasic()
.realmName("Provider API")
.and()
.addFilterBefore(testAuthFilter, BasicAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(
"/test/**"
)
.authenticated()
.anyRequest()
.permitAll()
;
}
}
TestAuthFilter.java:
#Component
public class TestAuthFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// TODO: Later implement via SecurityContextHolder.getContext().setAuthentication();
chain.doFilter(request,response);
}
}
I found solution which provides two independent entry points for authentication in the official documentation: Spring Security: 5.7 Multiple HttpSecurity
Here is the solution:
MultiHttpSecurityConfig.java
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Configuration
#Order(1)
public static class RestApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/rest/**")
.authorizeRequests()
.anyRequest().hasAuthority(Role.ROLE_USER.getAuthority())
.and()
.httpBasic()
.realmName("Rest API")
.and().csrf().disable()
;
}
}
#Configuration
#Order(2)
public static class TestWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/test**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.realmName("Test zone");
}
}
}
Related
Now there is authentication only once when I open my browser. I want to authenticate each request without need to reopen the browser.
This is my configuration class. What can I add to configure function?
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").hasRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().csrf().disable().httpBasic();
}
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("").password("{noop}").roles("ADMIN");
}
}
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource; // get by Spring
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// Here, you are making the public directory on the classpath root available without authentication (e..g. for css files)
.antMatchers("/public/**", "/registration.html").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.successHandler((request, response, authentication) -> new DefaultRedirectStrategy().sendRedirect(request, response, "/welcome"))
.failureUrl("/login-error.html")
.permitAll()
.and()
.logout()
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.permitAll();
}
}
Here is my custom handler: CustomLogoutSuccessHandler.java
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
private static Logger logger = LogManager.getLogger(CustomLogoutSuccessHandler.class);
#Override
public void onLogoutSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
logger.info("onLogoutSuccess:");
response.sendRedirect(request.getContextPath() + "/login.html");
}
}
In my template (users.html) I add a link like this:
<p align="center"><a th:href="#{/logout.html}">Logout</a></p>
But when click the logout link the method onLogoutSuccess is not call on my custom handler class.
Why?
I have a problem regarding on the controllers request from a spring boot application.
I have made a certificate in order to run the app on https. The certificate works fine, it is valid.
My main problem is when i test my methods from the controller through postman they(the url reques) work fine on https and http...it shouldn't work on http. Can someone help on this ?
This is my WebSecurityConfig class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
public static final String AUTHENTICATED_HEADER_NAME = "Authenticated";
public static final String AUTHENTICATED_TRUE = "true";
public static final String AUTHENTICATED_FALSE = "false";
#Autowired
public void globalUserDetails(final AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
auth.userDetailsService(authenticationManager).passwordEncoder(passwordEncoder);
}
#Override
#Bean(value = "authenticationManagerBean")
public org.springframework.security.authentication.AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Configuration
#Order(1)
public static class HTTPBasicSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
//todo check how we can change the root url of swagger
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/documentation**", "/configuration/**", "/v2/api-docs**", "/swagger-ui.html", "/webjars/**", "/swagger-resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
//todo http basic allows access to all urls after login
http
.httpBasic()
.and()
.csrf().disable()
.antMatcher("/api/**")
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest()
.authenticated();
}
}
#Configuration
#Order(2)
public static class FormLoginSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
//todo more investigation is required to check if it is safe to ignore csrf for login
.ignoringAntMatchers("/login")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.successHandler((httpServletRequest, httpServletResponse, authentication) -> {
httpServletResponse.setHeader(AUTHENTICATED_HEADER_NAME, AUTHENTICATED_TRUE);
})
.failureHandler((httpServletRequest, httpServletResponse, e) -> {
httpServletResponse.setHeader(AUTHENTICATED_HEADER_NAME, AUTHENTICATED_FALSE);
httpServletResponse.setStatus(SC_UNAUTHORIZED);
})
.and()
.logout().permitAll()
.and()
.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class)
.addFilterBefore(new CsrfHeaderFilter(), CsrfFilter.class);
http.exceptionHandling().authenticationEntryPoint((HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) -> {
if (authException != null) {
response.setStatus(SC_UNAUTHORIZED);
}
});
}
}
#Configuration
#Order(3)
public static class TestClass extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.httpStrictTransportSecurity()
.includeSubDomains(true)
.maxAgeInSeconds(31536000);
}
}
}
and this is my spring boot version
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.2.RELEASE</version>
<relativePath></relativePath>
</parent>
Your question is touching on several points:
you can require clients to require secure channels, by adding the security.require_ssl=true configuration property (see the Spring Boot reference documentation about HTTPS)
or use the following configuration snippet http.requiresChannel().anyRequest().requiresSecure();
you might want to enforce that as well with HSTS in Spring Security
None of the above helped the situation I was in.
I figured out that chrome (postman) was automatically transforming my http requests to https.
On the other browsers http requests didn't worked.
I am trying to use Spring Security and I have a use case where I want different login pages and different set of URLs to be secured.
Here is my configuration:
#Configuration
#Order(1)
public static class ProviderSecurity extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/admin/login").permitAll()
.antMatchers("/admin/**").access("hasRole('BASE_USER')")
.and()
.formLogin()
.loginPage("/admin/login").permitAll()
.defaultSuccessUrl("/admin/home")
.failureUrl("/admin/login?error=true").permitAll()
.usernameParameter("username")
.passwordParameter("password")
.and()
.csrf()
.and()
.exceptionHandling().accessDeniedPage("/Access_Denied");
}
}
#Configuration
#Order(2)
public static class ConsumerSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/consumer/login").permitAll()
.antMatchers("/consumer/**").access("hasRole('BASE_USER')")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/consumer/login").permitAll()
.defaultSuccessUrl("/consumer/home")
.failureUrl("/consumer/login?error=true").permitAll()
.usernameParameter("username")
.passwordParameter("password")
.and().csrf()
.and()
.exceptionHandling().accessDeniedPage("/Access_Denied");
}
}
These classes are inner classes of another class MultipleHttpSecurityConfig that has annotation #EnableWebSecurity.
The security for admin/** is working fine, but none of the consumer/** pages are secured, no redirection is happening for login page. I've searched for other answers but none worked.
Look at the Spring Security Reference:
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) { 1
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
#Configuration
#Order(1) 2
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**") 3
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
#Configuration 4
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}
1 Configure Authentication as normal
2 Create an instance of WebSecurityConfigurerAdapter that contains #Order to specify which WebSecurityConfigurerAdapter should be considered first.
3 The http.antMatcher states that this HttpSecurity will only be applicable to URLs that start with /api/
4 Create another instance of WebSecurityConfigurerAdapter. If the URL does not start with /api/ this configuration will be used. This configuration is considered after ApiWebSecurityConfigurationAdapter since it has an #Order value after 1 (no #Order defaults to last).
Your second configuration is not used, because your first configuration matches /** (no antMatcher configured). And your first configuration restricts only /admin/**, all other URLs are permitted by default.
Your first WebSecurityConfigurerAdapter's
http
.authorizeRequests()
matches all the URLs, limit it to only URLs start with /admin by using antMatcher:
#Configuration
#Order(1)
public static class ProviderSecurity extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/admin/**")
.authorizeRequests()
.antMatchers("/admin/login").permitAll()
.antMatchers("/admin/**").access("hasRole('BASE_USER')")
.and()
...
Edit: Solved. See my comment after this post
I am currently implementing a Webapplication with Spring-Security. I have implemented a custom AuthenticationFailureHandler which checks if a user tried to login with wrong credentials too often (and blocks him for serveral minutes). But normal failed logins should redirect the user to the login page with the parameter error (/login?error). This page shows an error message like "The password you typed in was wrong"
The AutenticationFailureHandler looks like this (without the uninteressting linse of code)
public class CustomAuthenticationHandler implements AuthenticationFailureHandler {
// Some variables
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// some logic here..
request.setAttribute("param", "error");
response.sendRedirect("/login?error");
}
My WebApplicationSecurity class looks like this:
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationHandler customAuthenticationHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
.permitAll()
.failureHandler(customAuthenticationHandler)
.and()
.logout()
.permitAll();
http.authorizeRequests()
.antMatchers("/css/**", "/img/**", "/js/**")
.permitAll()
.anyRequest()
.authenticated();
http
.csrf()
.disable();
}
#Bean
CustomAuthenticationHandler authenticationHandler() {
return new CustomAuthenticationHandler();
}
#Configuration
protected static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("*******")
.password("*******")
.roles("USER");
}
}
}
The problem now is that the AuthenticationFailureHandler redirects to /login?error but (i don't know why) another redirect is done to /login.
Can you help me to solve my problem?
Well, i solved it by adding "/login**" to http.authorizeRequests().antMatchers("/css/**", "/img/**", "/js/**")