AuthenticationEntryPoint don't handle LockedException - java

The AuthenticationEntryPoint only handle InsufficientAuthenticationException.
I must handle LockedException to get the error message with custom message.
But if I verify the exception given to the entry point it is of class InsufficientAuthenticationException. I dont throw the LockedException. I should throw it from any filter?
The code is:
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#SneakyThrows
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException {
HttpStatus status = HttpStatus.FORBIDDEN;
ErrorResponse errorResponse = ApiExceptionHandler.createErrorResponse(status)
.error("Authentication Error")
.message(e.getMessage())
.build();
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String errorJson = objectMapper.writeValueAsString(errorResponse);
PrintWriter printWriter = response.getWriter();
printWriter.println(errorJson);
printWriter.flush();
printWriter.close();
}
}
My spring security configuration:
#EnableWebSecurity
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserService userService;
private final DatabaseUserDetailsService databaseUserDetailsService;
private final DatabaseUserDetailsPasswordService databaseUserDetailsPasswordService;
private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
private final SecretKey secretKey;
private final JwtConfig jwtConfig;
public SecurityConfiguration(#Lazy UserService userService,
DatabaseUserDetailsService databaseUserDetailsService,
DatabaseUserDetailsPasswordService databaseUserDetailsPasswordService,
CustomAuthenticationEntryPoint customAuthenticationEntryPoint,
SecretKey secretKey,
JwtConfig jwtConfig) {
this.userService = userService;
this.databaseUserDetailsService = databaseUserDetailsService;
this.databaseUserDetailsPasswordService = databaseUserDetailsPasswordService;
this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;
this.secretKey = secretKey;
this.jwtConfig = jwtConfig;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProviderBean())
.userDetailsService(databaseUserDetailsService)
.userDetailsPasswordManager(databaseUserDetailsPasswordService)
.passwordEncoder(passwordEncoder());
}
#Bean
public AuthenticationProvider authenticationProviderBean() {
CustomAuthenticationProvider customAuthenticationProvider = new CustomAuthenticationProvider(userService);
customAuthenticationProvider.setPasswordEncoder(passwordEncoder());
customAuthenticationProvider.setUserDetailsService(databaseUserDetailsService);
return customAuthenticationProvider;
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(
"/favicon.ico",
"/robots.txt",
"/manifest.webmanifest",
"/sw.js",
"/offline.html",
"/icons/**",
"/images/**",
"/styles/**",
"/frontend/**",
"/frontend-es5/**", "/frontend-es6/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(new JwtUsernamePasswordAuthFilter(authenticationManager(), jwtConfig,
secretKey))
.addFilterAfter(new JwtTokenVerifier(secretKey, jwtConfig), JwtUsernamePasswordAuthFilter.class)
.authorizeRequests()
.antMatchers(HttpMethod.POST, Mappings.USER).permitAll()
.anyRequest()
.authenticated();
}
}

Related

Cycle between AuthenticationConfiguration and Filters

I have a problem with cycle injection and don't know how to fix it :(
For my SecurityConfig I need JWTAuthenticationFilter.
For my JWTAuthenticationFilter I need AuthenticationManager.
For my AuthenticationManager I need SecurityConfig.
Can you help me with resolving this problem ? Thanks !
#Configuration
#RequiredArgsConstructor
public class ApplicationConfig {
private final UserRepository userRepository;
#Bean
public UserDetailsService userDetailsService() {
return username -> userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth ) throws Exception {
return auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder()).and().build();
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfig {
private final JWTVerifierFilter jwtVerifierFilter;
private final AuthenticationProvider authenticationProvider;
private final JWTAuthenticationFilter jwtAuthenticationFilter;
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeHttpRequests()
.requestMatchers("/auth/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authenticationProvider(authenticationProvider)
.addFilter(jwtAuthenticationFilter)
.addFilterAfter(jwtVerifierFilter, JWTAuthenticationFilter.class);
return http.build();
}
}
#RequiredArgsConstructor
#Component
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final TokenRepository tokenRepository;
private final JwtService jwtService;
private final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
JwtAuthenticationRequest authModel = OBJECT_MAPPER.readValue(request.getInputStream(), JwtAuthenticationRequest.class);
Authentication authentication = new UsernamePasswordAuthenticationToken(authModel.getUsername(), authModel.getPassword());
return authenticationManager.authenticate(authentication);
} catch (IOException e) {
throw new AssertionError(e.getMessage(), e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {
String token = jwtService.generateToken((UserEntity) authResult.getPrincipal());
TokenEntity tokenEntity = TokenEntity.builder()
.id(UUID.randomUUID().toString())
.authenticationToken(token)
.username(authResult.getName())
.build();
tokenEntity = tokenRepository.save(tokenEntity);
response.addHeader(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", tokenEntity.getId()));
ConnValidationResponse respModel = ConnValidationResponse.builder().isAuthenticated(true).build();
response.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
response.getOutputStream().write(OBJECT_MAPPER.writeValueAsBytes(respModel));
}
}
I tried to replace
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
by
#Bean
public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
return authenticationManagerBuilder.build();
}
but for HttpSecurity I need SecurityConfig too.
I also tried add #Lazy for AuthenticationConfiguration config but it doesn't work

how to sign in using both basic authentication with own set of users and ldap authentication?

For now I have working authentication system, and now trying to add here ldap authentication.
First I should try to signin using my set of users, then if not found, try from ldap server.
But i don't have no idea how to do it.
AuthController:
#RestController
#RequestMapping("/api/v1/auth")
public class AuthController {
#Autowired
AuthenticationManager authenticationManager;
#Autowired
PasswordEncoder encoder;
#Autowired
JwtUtils jwtUtils;
#Autowired
UserService userService;
#PostMapping("/signin")
public ResponseEntity<JwtResponse> authenticateUser(#Valid #RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
if (!userDetails.getIsActive()) {
throw new RedoException("Данный пользователь заблокирован");
}
String jwt = jwtUtils.generateJwtToken(authentication);
List<String> permissions = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
Set<String> groups = userService.getAllGroupsByUser(userDetails.getUsername());
return ResponseEntity.ok(new JwtResponse(jwt,
userDetails.getUsername(),
userDetails.getEmail(),
permissions,
userService.getUserInfoByUsername(userDetails.getUsername()),
groups));
}
}
And WebSecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Autowired
UserDetailsServiceImpl userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedOriginPatterns(Collections.singletonList("*")); //set access from all domains
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Configuration
#Order(1)
public class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.antMatcher("/api/v1/external/**")
.authorizeRequests()
.anyRequest().authenticated().and()
.httpBasic();
}
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
#Configuration
#Order(2)
public class JwtAuthConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/api/v1/auth/**", "/api/v1/doc/", "/api/v1/check/", "/swagger-ui/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
}
and i used other project to test only ldap:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource()
.url("ldap://localhost:8389/dc=springframework,dc=org")
.and()
.passwordCompare()
.passwordEncoder(new BCryptPasswordEncoder())
.passwordAttribute("userPassword");
}
}
some application properties:
spring.ldap.embedded.ldif=classpath:test-server.ldif
spring.ldap.embedded.base-dn=dc=springframework,dc=org
spring.ldap.embedded.port=8389
So how use them both.

Access denied after changed password with spring boot security JWT

I'm implementing spring boot security for my application. The login function works fine, but somehow whenever I test the change-password function, it's always failed, I checked in the database the password has been updated with the new HashString, but I cannot login with both old-password and new-password. Please help !
Here is the change-password API
#PutMapping("/reset-password")
public ResponseEntity resetPassword(#RequestBody String password){
//extract user name
String username = SecurityContextHolder.getContext().getAuthentication()
.getPrincipal().toString();
ApplicationUser applicationUser = applicationUserRepo.findByUsername(username);
if(applicationUser == null ){
return ResponseEntity.badRequest().body("Could not find UserName");
}
applicationUser.setPassword(bCryptPasswordEncoder.encode(password));
applicationUserRepo.save(applicationUser);
Logs log = new Logs (TimeConverter.getVietnamCurrentTime(),"PASSWORD", "User", applicationUser.getUsername(), applicationUser.getUsername() );
logsRepo.save(log);
return ResponseEntity.ok().body("Password changed successfully!");
}
Here is my implementation of WebSecurityConfigurerAdapter
#EnableWebSecurity
#Configuration
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsServiceImpl userDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsServiceImpl userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, LOGIN_URL).permitAll()
//.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(ImmutableList.of("*"));
configuration.setAllowedMethods(ImmutableList.of("HEAD",
"GET", "POST", "PUT", "DELETE", "PATCH"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
AuthenticationFilter
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
ApplicationUser creds = new ObjectMapper()
.readValue(req.getInputStream(), ApplicationUser.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
System.out.println("fucked");
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) {
String token = JWT.create()
.withSubject(((User) auth.getPrincipal()).getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.sign(HMAC512(SECRET.getBytes()));
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
res.addHeader("Access-Control-Expose-Headers","*");
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
response.getWriter().write("blalfaf");
super.unsuccessfulAuthentication(request, response, failed);
}
}
Try using:
addFilterBefore(new JWTAuthenticationFilter(authenticationManager(), UsernamePasswordAuthenticationFilter.class)
instead of just
addFilter(....
in your WebSecurity.configure(.....
This will put your own filter before the original UsernamePasswordAuthenticationFilter. Maybe your filter is last in line and the login is failing before.

Performing authentication via spring security oauth2

I configure oauth2(resource server and auth server) in my spring boot app, but how to perform authentication now? How to use grants which I described in authentication server?
And how to perform autologin when new user has been registered?
#Configuration
public class OAuth2ServerConfig {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Inject
private Http401UnauthorizedEntryPoint authenticationEntryPoint;
#Inject
private AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/authorize"))
.disable()
.headers()
.frameOptions().disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/admin").hasAnyAuthority("ADMIN");
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static final String CLIENTID = "app";
private static final String PROP_SECRET = "secret";
private static final Integer TOKEN_VALIDITY_SECONDS = -1;
#Inject
private OAuth2AccessTokenRepository oAuth2AccessTokenRepository;
#Inject
private OAuth2RefreshTokenRepository oAuth2RefreshTokenRepository;
#Bean
public TokenStore tokenStore() {
return new MongoDBTokenStore(oAuth2AccessTokenRepository, oAuth2RefreshTokenRepository);
}
#Inject
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(CLIENTID)
.scopes("read", "write")
.authorities("USER", "ADMIN")
.authorizedGrantTypes("password", "refresh_token")
.secret(PROP_SECRET)
.accessTokenValiditySeconds(TOKEN_VALIDITY_SECONDS);
}
}
}
You should have something like this:
#Component
public class CustomAuthenticationProvider
implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if (shouldAuthenticateAgainstThirdPartySystem()) {
// use the credentials
// and authenticate against the third-party system
return new UsernamePasswordAuthenticationToken(
name, password, new ArrayList<>());
} else {
return null;
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(
UsernamePasswordAuthenticationToken.class);
}
}
and register it to your SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
#Override
protected void configure(
AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic();
}
}

Spring security OAuth stackoverflowException

I would like to use Spring security with OAuth and JWT tokens.
My current configurations are:
#Configuration
#EnableResourceServer
public class OAuth2ServerConfig {
#Configuration
#EnableWebSecurity
protected static class ResourceServer extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.anonymous().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler()) // handle access denied in general (for example comming from #PreAuthorization
.authenticationEntryPoint(entryPointBean()) // handle authentication exceptions for unauthorized calls.
.and()
.authorizeRequests()
// only allow this three endpoint to NOT be authenticated.
.antMatchers(HttpMethod.POST, "/users").permitAll()
.antMatchers(HttpMethod.POST, "/users/authenticate").permitAll()
.antMatchers(HttpMethod.GET, "/users/inviationCode/{code}").permitAll()
.antMatchers(HttpMethod.POST, "/**").fullyAuthenticated()
.antMatchers(HttpMethod.GET, "/**").fullyAuthenticated()
.antMatchers(HttpMethod.PUT, "/**").fullyAuthenticated()
.antMatchers(HttpMethod.DELETE, "/**").fullyAuthenticated()
.antMatchers(HttpMethod.OPTIONS, "/**").fullyAuthenticated()
.and()
.addFilterBefore(filterBean(), AbstractPreAuthenticatedProcessingFilter.class)
.requestMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher("/oauth/**")))
.authorizeRequests().anyRequest().authenticated().expressionHandler(new OAuth2WebSecurityExpressionHandler())
.and()
.csrf().disable(); // for chrome/FF plugins to work. for now we shouldn't face any problem since there is no point that JS can be injected into our page...
// #formatter:on
}
#Bean(name="authenticationManager")
#Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManagerBean();
}
#Bean
#Autowired
AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedExceptionHandler();
}
#Bean
#Autowired
AuthenticationEntryPoint entryPointBean() {
return new UnauthorizedEntryPoint();
}
#Bean
#Autowired
GenericFilterBean filterBean() {
return new XAuthTokenFilter();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean(name="userAuthenticationManager")
public UserAuthenticationService userAuthenticationManager() throws Exception {
return new UserAuthenticationService();
}
}
#Configuration
#EnableAuthorizationServer
public static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManager")
private AuthenticationManager authenticationManager;
#Autowired
#Qualifier("restDataSource")
private BasicDataSource restDataSource;
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
return new JwtAccessTokenConverter();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess(
"hasAuthority('ROLE_TRUSTED_CLIENT')");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).accessTokenConverter(accessTokenConverter());
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("test")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write")
.secret("secret");
}
}
}
Those configurations are based on the official spring github repo
The problem I am facing now, is that whenever I try to obtain a token using this url:
http://myapplication.com/test/oauth/token?grant_type=password
I am getting the following error:
java.lang.StackOverflowError
at org.apache.commons.logging.impl.Jdk14Logger.isDebugEnabled(Jdk14Logger.java:214)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:144)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:427)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:427)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:427)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
This seems like a loop in the authentication process but to be honest I can find the root of it.
The flow I would like to use is the following:
User asks for a token passing user name, password and client (probably in base64). (METHOD POST)
User is being authenticated.
User is returned a JWT token.
User carries the token in the header.
Can someone advice on the appropriate configurations?
Best
Found the problem, it was specific to the authentication manager.
This is the working configuration for me:
#Configuration
#ComponentScan
#EnableResourceServer
#Import({SecurityConfig.class})
public class OAuth2ServerConfig {
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
#Qualifier("restDataSource")
private DataSource datasource;
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
return new JwtAccessTokenConverter();
}
#Bean
public CustomJwtTokenStore tokenStore() {
return new CustomJwtTokenStore();
}
// #Bean
// public JdbcTokenStore tokenStore() {
// return new JdbcTokenStore(datasource);
// }
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess(
"hasAuthority('ROLE_TRUSTED_CLIENT')");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore()).accessTokenConverter(accessTokenConverter());
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(60)
.and()
.withClient("my-client-with-registered-redirect")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT")
.scopes("read", "trust")
.redirectUris("http://anywhere?key=value")
.and()
.withClient("my-client-with-secret")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write")
.secret("secret");
}
}
}
with the security config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Autowired
private GenericFilterBean filter;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/webjars/**", "/images/**", "/oauth/uncache_approvals", "/oauth/cache_approvals");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userAuthenticationManager()).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler) // handle access denied in general (for example comming from #PreAuthorization
.authenticationEntryPoint(authenticationEntryPoint) // handle authentication exceptions for unauthorized calls.
.and()
.authorizeRequests()
.antMatchers("/xxx/**").fullyAuthenticated()
.antMatchers("/xxx/**").fullyAuthenticated()
.antMatchers("/xxx/**").fullyAuthenticated()
.and()
.csrf().disable();;
}
#Bean
#Autowired
ApplicationListener<AbstractAuthenticationEvent> loggerBean() {
return new org.springframework.security.authentication.event.LoggerListener();
}
#Bean
#Autowired
AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedExceptionHandler();
}
#Bean
#Autowired
AuthenticationEntryPoint entryPointBean() {
return new UnauthorizedEntryPoint();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean(name="userAuthenticationManager")
public UserDetailsService userAuthenticationManager() throws Exception {
return new UserServiceImpl();
}
#Bean
public UserService userService() {
return new UserServiceImpl();
}
}

Categories