The route /gateways needs authentication.
When /gateways is accessed in the browser I am redirected to /login and the following form appears:
If /gateways is accessed from an angular2 app the following popup appears:
My spring security configuration is the following:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static String REALM="Authentication";
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("cris").password("123").roles("ADMIN");
auth.inMemoryAuthentication().withUser("felix").password("felix123").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.formLogin()
.and()
.authorizeRequests()
.antMatchers("/user", "/vehicles", "/signin", "/isautheticated").permitAll().anyRequest()
.authenticated();
}
// Access-Control-Allow-Origin header to be present
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
/* To allow Pre-flight [OPTIONS] request from browser */
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
So how can the popup be disabled?
You should specify formLogin() instead of httpBasic() in your configuration. your configure method should look like this.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login");
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.formLogin()
.and()
.authorizeRequests()
.antMatchers("/user", "/vehicles", "/signin", "/isautheticated").permitAll().anyRequest()
.authenticated();
}
I think your request from angular2 is taking an invalid Authorization Basic header, it was handled by BasicAuthenticationFilter, and it threw a AuthenticationException, and start to entry point.
You can implement your own entry point that implements AuthenticationEntryPoint, and then inject to BasicFilter, the default entry point is BasicAuthenticationEntryPoint. As you can see, it will return a WWW-Authenticate response header.
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
authException.getMessage());
}
Related
I am trying to change default POST /login spring security endpoint for logging in to POST /api/users/login
I already tried doing that through
formLogin().loginProcessingUrl("/api/users/login")
or
formLogin().loginPage("/api/users/login")
but it didn't work. How do i do that? I can't find tutorial describing it or stack overflow answer. I also tried reading Spring Security documentation but it didn't help too.
My security config looks like this:
private final MyUserDetailsService myUserDetailsService;
private final PasswordEncoder passwordEncoder;
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//go from most restrictive url to least restrictive, from single urls to /**
httpSecurity.csrf().disable().cors().and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//to allow h2 console page
httpSecurity.headers().frameOptions().disable();
}
In the WebSecurityConfigurerAdapter class there is to configure methods that you should override like so:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(
unAuthorizedResponseAuthenticationEntryPoint)
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
http.addFilterBefore(authenticationTokenFilter,
UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring().antMatchers(HttpMethod.POST, "/api/auth/login")
.antMatchers(HttpMethod.OPTIONS, "/**")
.and().ignoring()
.antMatchers(HttpMethod.GET, "/").and().ignoring();
}
}
Please see in method:
public void configure(WebSecurity webSecurity) throws Exception
the line:
webSecurity.ignoring().antMatchers(HttpMethod.POST, "/api/auth/login")
This is where you put your custom authentication API endpoint!
And then you may proceed as I explained in the following question
How to use custom UserDetailService in Spring OAuth2 Resource Server?
I added two filter like below
JwtUsernameAndPasswordAuthenticationFilter
JwtTokenVerifier
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/makestory").hasAnyRole("OLDBIE")
.and()
.addFilter(new JwtUsernameAndPasswordAuthenticationFilter())
.addFilterAfter(new JwtTokenVerifier(), JwtUsernameAndPasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginErroHandler());
}
and but I want to exclude some of urls like /main/, /main/story/, etc
so I was expected that /main/, /main/story/ urls don't need to be censored
so I added below code
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/main/**");
}
but there's a problem. A request has been passed to JwtUsernameAndPasswordAuthenticationFilter class
public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException{
log.info("JwtUsernameAndPasswordAuthenticationFilter is actived");
}
}
I don't want to a request be passed into JwtUsernameAndPasswordAuthenticationFilter
what should I do?
Update:
_____ Only to bypass Security _____
You may add matchers and permit before asking authentication for any other request
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.
.
.
.antMatchers("/main/**")
.permitAll()
.anyRequest()
.authenticated()
.
.
}
this part may not be necessary only for bypass auth.
public void configure(WebSecurity web)
______ apply filter to only matching _______
#Bean
public FilterRegistrationBean<JwtUsernameAndPasswordAuthenticationFilter> loggingFilter() {
FilterRegistrationBean<JwtUsernameAndPasswordAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new JwtUsernameAndPasswordAuthenticationFilter());
registrationBean.addUrlPatterns("/home**", "/test", "/sample/**");
return registrationBean;
}
I have two types of users: Application User and End User and I have separate tables for these. Now, I want to apply security on these two tables.
I provided custom implementation of UserDetailsService for Application users:
#Component("applicationUserDetailsService")
public class ApplicationUserDetailsService implements UserDetailsService {}
And, I provided another second custom implementation of UserDetailsService for End users:
#Component("endUserDetailsService")
public class EndUserDetailsService implements UserDetailsService {}
Now, in the following code snippet, I have registered two endpoints for both type of users. I have injected both implementation of UserDetailsService and registered by #Overide configure(AuthenticationManagerBuilder auth) method for both application and end user separately.
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Import(SecurityProblemSupport.class)
public class SecurityConfiguration {
// Injected via Constructor Injection
private final EndUserDetailsService endUserDetailsService;
private final ApplicationUserDetailsService applicationUserDetailsService;
#Configuration
#Order(1)
public class ApplicationUserSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("/api/customer/**")
.authorizeRequests()
.antMatchers("/api/customer/authenticate").permitAll()
.antMatchers("/api/customer/**")
.authenticated()
.and()
.apply(securityConfigurerAdapter());
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(endUserDetailsService);
}
}
//no #Order defaults to last
#Configuration
public class EndUserSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.apply(securityConfigurerAdapter());
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(applicationUserDetailsService);
}
}
private JWTConfigurer securityConfigurerAdapter() {
return new JWTConfigurer(tokenProvider);
}
}
And, I'm trying to authenticate the user like this:
//Injected via Constructor Injection
private final AuthenticationManagerBuilder authenticationManagerBuilder;
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
When the above code snippet is executed, I get the Null Pointer Exception because authenticationManagerBuilder.getObject() returns NULL. And when I use just when implementation of UserDetailService with #Component("userDetailsService") and not set UserDetailService in security config like auth.userDetailsService("..."), it works fine but by that way I can't achieve authentication from multiple tables.
What I want to Achieve:
In simple words, I want spring security to authenticate user from two tables.
requestMatchers() is the call that you need as it allows you to isolate adapters by URL:
#Order(1)
#EnableWebSecurity
class EndUserConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/api/customer/**")
.and()
.authorizeRequests()
.antMatchers("/**").hasRole("CUSTOMER")
.and()
.apply(yourJointConfigurations());
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(endUserDetailsService);
}
}
Regarding calling the AuthenticationManager directly, it would be ideal if you could rely on the existing filter chain to do the work for you. For example, since you are stateless, HTTP Basic might be a better fit for you, which you could apply to both configurations, instead of trying to have a dedicated /authenticate endpoint.
In my project, i just want to allow oauth login. my spring security config as follow:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/login.html", "/oauth/**", "/sign**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().disable()
.httpBasic().disable()[enter image description here][1]
.exceptionHandling().accessDeniedPage("/login.html")
.and()
.apply(new SpringSocialConfigurer());
}
The problem is the access denied page not working. I want to get login.html content as follow:
login.html
in fact i got 403 page as follow:
403
How to solve this problem?
I solve this problem, the code as follow:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login.html", "/oauth/**", "/sign**").permitAll()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(new HttpForbiddenEntryPoint(), AnyRequestMatcher.INSTANCE)
.and()
.logout()
.logoutUrl("/logout.html")
.clearAuthentication(true)
.logoutSuccessUrl("/login.html")
.and()
.apply(new SpringSocialConfigurer());
}
The entry point:
public class HttpForbiddenEntryPoint implements AuthenticationEntryPoint {
private String redirect_url = "/login.html";
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendRedirect(redirect_url);
}
public String getRedirect_url() {
return redirect_url;
}
public void setRedirect_url(String redirect_url) {
this.redirect_url = redirect_url;
}
}
I'm using spring-boot (1.3.5) + oauth2 (spring-cloud-starter-oauth2). I would like to test some LogoutSuccessHandler but I'm unable to invoke it.
This is my security configuration:
#Configuration
#EnableWebSecurity
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private LogoutHandler logoutHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.httpBasic().disable()
.authorizeRequests()
.antMatchers("/index.html", "/", "/resources/**", "/css/**").permitAll()
.anyRequest().authenticated();
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessHandler(logoutHandler);
//.invalidateHttpSession(true).deleteCookies("JSESSIONID", "SESSION");
}
}
This is my logoutHandler
#Component
public class LogoutHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
setDefaultTargetUrl("/");
super.handle(request, response, authentication);
}
}
When I debug the application and put the breakpoint to the logoutHandler it's never invoked. Am I missing something in this configuration?
Thanks
Update your configure method a little bit. You don't have to use AntPathRequestMatcher to match logout url.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.httpBasic().disable()
.authorizeRequests()
.antMatchers("/index.html", "/", "/resources/**", "/css/**").permitAll()
.anyRequest().authenticated()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(logoutHandler)
//.deleteCookies("JSESSIONID", "JSESSIONID")
//.invalidateHttpSession(true)
.permitAll();
}