I've a problem with exception handling during authentication using Spring Security.
This is my AuthenticationProvider where on every exception an AuthenticationServiceException is thrown.
#Component
public class MyAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
try {
// ...
} catch (Exception e) {
throw new AuthenticationServiceException(e.getMessage(), e);
}
}
}
Below my custom AuthenticationProvider.
#Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
log.debug(e.toString());
}
}
This is the security config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint);
}
}
Everything is triggered as expected. The problem is that in the AuthenticationProvider the AuthenticationException is an instance of InsufficientAuthenticationException and not AuthenticationServiceException as thrown by the AuthenticationProvider.
What I want instead in MyAuthenticationEntryPoint is the exception thrown with the cause set, which is a custom exception.
How can I solve this?
Why Spring replace an AuthenticationServiceException with an InsufficientAuthenticationException?
Thanks in advance.
SOLUTION
I found the solution! The problem is in SecurityConfig class. .authenticationEntryPoint(authenticationEntryPoint) must be under .httpBasic() and not set globally.
The right configuration is the following:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf().disable();
}
Related
I am going to set up background Spring Authentication by a custom class that implemented UserDetailsService. As said in the documentation method loadUserByUsername calls each time when user try to login, but it don't in the case below.
#Component
public class AuthenticationUserDetailsService implements UserDetailsService {
#Autowired
private PersonRepository personRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Person> personByUserName = personRepository.findPersonByUserName(username);
Person person = personByUserName.
orElseThrow(() -> new UsernameNotFoundException("User with username has not found"));
return new User(username, person.getPassword(), Collections.emptyList());
}
}
I try to use several alternatives to set up configure(HttpSecurity http) method in WebSecurityConfig class.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("api/person/login")
.usernameParameter("username")
.passwordParameter("password");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/index.html", "/", "/home", "/api/person/login").permitAll()
.anyRequest().authenticated();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.
authorizeRequests().
anyRequest().authenticated()
.and().
formLogin().
loginProcessingUrl("/login").
usernameParameter("username").
passwordParameter("password");
}
Registration DaoAuthenticationProvider:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(getAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider getAuthenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(authenticationUserDetailsService);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
return daoAuthenticationProvider;
}
Could anyone explain, where is error, and why loadUserByUsername method doesn't call at all?
The Angular 8 UI part uses to login.
public loginPerson(person: Person): Observable<Person> {
let url = "http://localhost:8080/login";
return this.httpClient.post<Person>(url, {userName: person.username, password: person.password});
}
I have a simple Spring Boot application with the following 2 endpoints:
int: requires Shibboleth SSO && Authorized Role
ext: no SSO, no authorization required
I've implemented a PreAuthenticationFilter to work with SSO. Below is
the configuration that is not working:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/ext/**").permitAll()
.anyRequest().authenticated()
.and()
.authorizeRequests()
.and()
.addFilter(preAuthenticationFilter());
}
}
Shouldn't PreAuthenticationFilter bypass the /ext endpoint? However, the above configuration forces both endpoints to go to the PreauthenticationFilter. Also tried
web.ignoring().antMatchers("/ext/**")
to no avail.
Here's the rest of my program:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/ext/**").permitAll()
.anyRequest().authenticated()
.and()
.authorizeRequests()
.and()
.addFilter(preAuthenticationFilter());
}
#Override
public void configure(WebSecurity web) throws Exception {
//web.ignoring().antMatchers("/ext/**");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
authenticationProvider.setPreAuthenticatedUserDetailsService(new ShibbolethUserDetailsService());
auth.authenticationProvider(authenticationProvider);
}
#Bean
RequestHeaderAuthenticationFilter preAuthenticationFilter() throws Exception {
ShibbolethRequestHeaderAuthenticationFilter filter = new ShibbolethRequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
I have a Vaadin application that is secured using spring security OAuth2. This works fine except for the occasional PUSH or HEARTBEAT endpoint being used to request first and thus triggering the auth process and the user ends up on the wrong page (These endpoints should not be visited directly by the user).
A simple but unsecure fix is to permitAll() on these endpoints. However as this poses a threat I need to close this hole up.
To do this I would like to parse and potentially edit the request url before redirecting to it at successfull auth. How would I go about doing this?
I would guess I need to add a filter somewhere in the chain to intercept the request and edit it. But I'm not sure where.
Here is my client:
#Configuration
#EnableOAuth2Sso
public class OAuthConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/vaadinServlet/PUSH/**").permitAll() //todo fix this hole
.antMatchers("/vaadinServlet/HEARTBEAT/**").permitAll() //todo fix this hole
.anyRequest().authenticated()
.and()
.logout()
.logoutSuccessUrl("/")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
#Override
public void configure(WebSecurity web) throws Exception
{
web.ignoring().antMatchers("/css/*").antMatchers("/VAADIN/**"); // Static resources are ignored
}
}
And the server:
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter
{
//jwt token stuff & my own client/auth providers. Should not be important.
...
}
server login form:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private RestAuthenticationProvider authenticationProvider;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/forgetPassword*").permitAll()
.antMatchers(HttpMethod.POST,"/user/resetPassword*").permitAll()
.antMatchers(HttpMethod.GET,"/user/changePassword*").permitAll()
.antMatchers("/user/updatePassword*", "/user/savePassword*", "/updatePassword*")
.hasAnyAuthority("CHANGE_PASSWORD_PRIVILEGE","ROLE_USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository()
{
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
Just add some implementation with your project
1: create Authentication Failure handler
#Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
System.out.print("here failure");
String s=request.getParameter("username");
setDefaultFailureUrl("/login?error&username="+s);
super.onAuthenticationFailure(request,response,exception);
}
}
2: Authentication Success Handler
#Component
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request , HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
/* custom Block
Do any thing here
*/
setDefaultTargetUrl("/home/");
super.onAuthenticationSuccess(request,response,authentication);
}
}
3: access request entry point
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
System.out.print("Unauthorized Access");
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
Implement the components as per your requirement.
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();
}
i'm needing help with this problem...
i can't secure my controllers in my security configuration files. but i can do it in my controller using
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
but this is really annoying, i want to do it from my security conf. files
this is my WebSecurityconfigurerAdapter:
#Configuration
//#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = false)
//#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
//#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
CustomUserDetailsService cuds;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(cuds)
.passwordEncoder(passwordEncoder())
.and()
.authenticationProvider(customAuthenticationProvider);
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**").authenticated()
.antMatchers("/test").authenticated()
.antMatchers("/usuarios/**").hasRole("ADMIN");
}
}
and this is my Oauth2Configuration:
#Configuration
public class Oauth2Configuration {
private static final String RESOURCE_ID = "restservice";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private CustomLogoutSuccessHandler customLogoutSuccessHandler;
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
// Logout
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler)
.and()
//Session management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
//URI's to verify
.authorizeRequests()
.antMatchers("/oauth/logout").permitAll()
.antMatchers("/**").authenticated()
.antMatchers("/usuarios/**").hasRole("ADMIN");
}
}
i've tried to use authority and roles, but nothings works. some idea what i'm doing wrong?
Well thanks to Yannic Klem i got the answer, was a problem with the order
First on my WebSecurityConfigurerAdapter i set my authentication on "usuarios"
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/usuarios").authenticated();
}
after that in my Oauth2Configuration set my authorizarion with my rol.
#Override
public void configure(HttpSecurity http) throws Exception {
http
// Logout
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler)
.and()
//Session management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
//URI's to verify
.authorizeRequests()
.antMatchers("/oauth/logout").permitAll()
.antMatchers("/usuarios/**").hasRole("ADMIN");
}
and now all works pretty fine. thank you all!