Property Source not getting autowired - spring-boot

I am using external property sources for some reason one of the external property source is not getting autowired, receiving null pointer while creating the authenticaion bean
Error Message
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.filechecker.check.Authenticator]: Constructor threw exception; nested exception is java.lang.NullPointerException
Caused by: java.lang.NullPointerException: null
at com.filechecker.check.Authenticator.(Authenticator.java:30) ~[classes!/:0.0.1-SNAPSHOT]
Line no 30:
String username = emailPropertyConfig.getEmailConfig().getUsername();
not working one
#Component
#PropertySource(value="${email.app.properties}",ignoreResourceNotFound = false)
#ConfigurationProperties
public class PropertyEmailConfiguration {
private EmailConfig emailConfig = new EmailConfig();
public EmailConfig getEmailConfig() {
return emailConfig;
}
public void setEmailConfig(EmailConfig emailConfig) {
this.emailConfig = emailConfig;
}
}
#Component
public class Authenticator extends javax.mail.Authenticator {
#Autowired
PropertyEmailConfiguration emailPropertyConfig;
#Autowired
CipherCrypt cipherCrypt;
private PasswordAuthentication authentication;
public Authenticator() throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
String username = emailPropertyConfig.getEmailConfig().getUsername();
String password = cipherCrypt.decrypt(emailPropertyConfig.getEmailConfig().getEncryptPassword());
authentication = new PasswordAuthentication(username, password);
}
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return authentication;
}
}
working one
#Component
#PropertySource(value="${external.app.properties}", ignoreResourceNotFound = true)
#ConfigurationProperties
public class PropertyConfiguration {
private List<FileStructureConfig> fileStructureConfig = new ArrayList();
private List<EmailSendingProperties> emailSendingProperties = new ArrayList();
public List<FileStructureConfig> getFileStructureConfig() {
return fileStructureConfig;
}
public void setFileStructureConfig(List<FileStructureConfig> fileStructureConfig) {
this.fileStructureConfig = fileStructureConfig;
}
public List<EmailSendingProperties> getEmailSendingProperties() {
return emailSendingProperties;
}
public void setEmailSendingProperties(List<EmailSendingProperties> emailSendingProperties) {
this.emailSendingProperties = emailSendingProperties;
}
}

You are trying to access an #Autowired property within the constructor. The property cannot be autowired at this stage.
In order for Spring to "bake the bean", Spring has to create your object (using your constructor), and only afterwards it applys the autowiring mechanism to inject emailPropertyConfig and cipherCrypt. Therefore you cannot access the two #Autowired properties within the constructor.
If you need to extract some values from the emailPropertyConfig or cipherCrypt you can do it in #PostConstruct
#Component
public class Authenticator {
#Autowired
PropertyEmailConfiguration emailPropertyConfig;
#Autowired
CipherCrypt cipherCrypt;
private PasswordAuthentication authentication;
#PostConstruct
void init() throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
String username = emailPropertyConfig.getEmailConfig().getUsername();
String password = cipherCrypt.decrypt(emailPropertyConfig.getEmailConfig().getEncryptPassword());
authentication = new PasswordAuthentication(username, password);
}
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return authentication;
}
}
or use constructor injection:
#Component
public class Authenticator {
PropertyEmailConfiguration emailPropertyConfig;
CipherCrypt cipherCrypt;
private PasswordAuthentication authentication;
public Authenticator(PropertyEmailConfiguration emailPropertyConfig, CipherCrypt cipherCrypt) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
String username = emailPropertyConfig.getEmailConfig().getUsername();
String password = cipherCrypt.decrypt(emailPropertyConfig.getEmailConfig().getEncryptPassword());
authentication = new PasswordAuthentication(username, password);
}
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return authentication;
}
}

Related

Spring boot SecurityContextHolder.getContext() NPE when using #Async in a #Scheduled service

I am using a scheduled service in spring boot app , i need to get the current connected user inside that service , my problem is that
SecurityContextHolder.getContext().getAuthentication()
returns the current connected user only once ( just after i am logged in ) , but in the next running tasks
SecurityContextHolder.getContext().getAuthentication()
returns NPE , i have searched and found that SecurityContextHolder is not shared outside the main thread.
My Service :
#Service
#EnableScheduling
public class SsePushNotificationService {
public void addEmitter(final SseEmitter emitter) {
emitters.add(emitter);
}
public void removeEmitter(final SseEmitter emitter) {
emitters.remove(emitter);
}
#Async("taskExecutor")
#Scheduled(fixedDelay = 5000)
public void doNotify() throws IOException {
System.out.println("------##### inside doNotify");
System.out.println("##### ---- curent thread /notification : " + Thread.currentThread().getName());
if (SecurityContextHolder.getContext().getAuthentication() != null) {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails) principal).getUsername();
System.out.println("------##### principal instanceof UserDetails : " + username);
} else {
String username = principal.toString();
System.out.println("------##### principal : " + username);
}
}
}
}
the controller :
#Controller
#CrossOrigin(origins = "*")
public class SsePushNotificationRestController {
#Autowired
SsePushNotificationService service;
#Autowired
UserDetailsServiceImpl userService;
#Autowired
UserNotificationService userNotifService;
final List<SseEmitter> emitters = new CopyOnWriteArrayList<>();
String username;
int nbrEvent;
#GetMapping(value = "/notification", produces = { MediaType.TEXT_EVENT_STREAM_VALUE })
public ResponseEntity<SseEmitter> doNotify() throws InterruptedException, IOException {
System.out.println("##### ---- curent thread /notification : " + Thread.currentThread().getName());
final SseEmitter emitter = new SseEmitter();
service.addEmitter(emitter);
service.doNotify();
emitter.onCompletion(() -> service.removeEmitter(emitter));
emitter.onTimeout(() -> service.removeEmitter(emitter));
return new ResponseEntity<>(emitter, HttpStatus.OK);
}
}
Javascript :
const eventSource = new EventSource('http://localhost:8080/notification');
eventSource.onmessage = e => {
const msg = e.data;
$("#notifCounter").text(msg);
$("#usrNotifCounter").text(msg);
};
eventSource.onopen = e => console.log('open');
eventSource.onerror = e => {
if (e.readyState == EventSource.CLOSED) {
console.log('close');
}
else {
console.log(e);
}
};
eventSource.addEventListener('second', function(e) {
console.log('second', e.data);
}, false);
WebSecurityConfig :
#Configuration
#EnableWebSecurity
public class WebSecurityConfig<S extends Session> extends WebSecurityConfigurerAdapter {
#Autowired
private FindByIndexNameSessionRepository<S> sessionRepository;
#Autowired
private MySessionExpiredStrategy sessionExpiredStrategy;
#Bean
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Component
public class MySessionExpiredStrategy implements SessionInformationExpiredStrategy {
#Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event)
throws IOException, ServletException {
HttpServletResponse response = event.getResponse();
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(
"Your account has been logged in elsewhere, and the current login has expired. If the password is leaked, please change it immediately!");
}
}
#Bean
public SpringSessionBackedSessionRegistry<S> sessionRegistry() {
return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
}
#Bean
public ConcurrentSessionControlAuthenticationStrategy sessionControlAuthenticationStrategy() {
ConcurrentSessionControlAuthenticationStrategy csas = new ConcurrentSessionControlAuthenticationStrategy(
sessionRegistry());
csas.setExceptionIfMaximumExceeded(true);
return csas;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/img/**", "/error");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
.anyRequest().access("#rbacService.hasPermission(request,authentication)")
.and().formLogin().loginPage("/login").defaultSuccessUrl("/", true).permitAll().and().logout()
.deleteCookies("JSESSIONID").invalidateHttpSession(true).clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login?logout")
.permitAll().and().exceptionHandling().accessDeniedPage("/static/403")
.and().sessionManagement().sessionFixation().migrateSession()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/static/invalidSession.html").maximumSessions(2).maxSessionsPreventsLogin(false)
.expiredSessionStrategy(sessionExpiredStrategy).sessionRegistry(sessionRegistry())
.expiredUrl("/login?invalid-session=true");
}
}
what is the best approch to share SecurityContextHolder between threads in that case.

Use sAMAccountName instead of userPrincipalName in LDAP auth in Spring Boot

So I am using the code below to connect to our LDAP server in my spring boot app, I can authorized successfully using the userPrincipalName -> (LastName.FirstName#enterprise.com), but I want to use the assigned sAMAccountName instead (ID00001).
I messed around with the setSearchFilter by doing: provider.setSearchFilter("(sAMAccountName ={0})")
but it's not working. (I'm getting bad credentials)
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/myapplication")
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("", "ldap://test.enterprise.com","dc=ORG1, dc=ORG2");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setSearchFilter("(sAMAccountName={0})"); // THIS DOES NOT WORK
provider.setSearchFilter("(userPrincipalName={0})"); // THIS WORKS
return provider;
}
}
EDIT: OK turns out I have been using the wrong field, there is another field: sAMAccountName that has the same value that I should be using, updated the title and question contents.
EDIT:
Ok I tried:
provider.setSearchFilter("(&(objectClass=user)(cn={0}))");
provider.setSearchFilter("(&(objectClass=user)(sAMAccountName={0}))");
provider.setSearchFilter("(&(objectClass=user)(cn={1}))");
provider.setSearchFilter("(&(objectClass=user)(sAMAccountName={1}))");
and it's still the same error,
bad credentials, AcceptSecurityContextError, 52e v2580
I managed to solve my issue but instead of using spring ldap dependencies, I created a custom authentication provider that implements AuthenticationProvider and and used the following code to connect to ldap and validate the credentials:
autowire it to WebSecurityConfig class.
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private UserDetails userDetails;
#Override
public Authentication authenticate(Authentication auth) throws
AuthenticationException {
String user = authentication.getName();
String pass = authentication.getCredentials.ToString();
try {
if (isRegistrered(user,pass) {
return new UsernamePasswordAuthenticationToken(user,pass, new
ArrayList<>());
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
private isRegisterd(String user, String pass) {
boolean result = false;
try {
// Set up the environment for creating the initial context
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://ldap_server:389");
//
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "DOMAIN\\USER");
env.put(Context.SECURITY_CREDENTIALS, "PASSWORD");
// Create the initial context
DirContext ctx = new InitialDirContext(env);
if(ctx != null) {
ctx.close();
}
return result;
} catch (Exception e) {
return result;
}
}
private void retrieveUserDetails(DirContext ctx, String username) throws NamingException {
String userSearchBase = "dc=TEST,dc=SAMPLE";
String userSearchFilter = String.format("sAMAccountName=%s", username);
//any attributes that you want
String[] attributes = { "sAMAccountName", "department" };
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningAttributes(attributes);
SearchResult result = null;
NamingEnumeration<?> users = ctx.search(userSearchBase, userSearchFilter, controls);
while(users.hasmore()) {
result = (SearchResult) users.next();
Attributes attr = result.getAttribtutes();
String sAMAccountName = attr.get("sAMAccountName").get(0).toString();
String department = attr.get("department").get(0).toString();
//assign to autowired object to be accessed anywhere
this.userDetails.setAccountName(sAMAccountName);
this.userDetails.setDepartment(department);
}
}
}
There's another approach by using LdapAuthenticationProvider. I use LdapAuthenticationProvider with a single parameter constructor.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Profile("integration")
public class SecurityConfigurationT extends WebSecurityConfigurerAdapter {
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final TokenProvider tokenProvider;
public SecurityConfigurationT(
AuthenticationManagerBuilder authenticationManagerBuilder,
TokenProvider tokenProvider) {
this.authenticationManagerBuilder = authenticationManagerBuilder;
this.tokenProvider = tokenProvider;
}
#PostConstruct
public void initIntegration() {
try {
authenticationManagerBuilder
.authenticationProvider(ldapAuthenticationProvider());
} catch (Exception e) {
throw new BeanInitializationException("Security configuration failed", e);
}
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public LdapAuthenticationProvider ldapAuthenticationProvider() throws Exception {
return new LdapAuthenticationProvider(ldapAuthenticator());
}
#Bean
public LdapContextSource ldapContextSource() throws Exception {
PasswordPolicyAwareContextSource contextSource = new PasswordPolicyAwareContextSource("ldaps://int.root.company.ag:636");
contextSource.setUserDn("CN=system_user,OU=companygroup svc accs,DC=int,DC=root,DC=company,DC=ag");
contextSource.setPassword("XXXXXX");
return contextSource;
}
// Use this for other filter such as "sAMAccountName".
#Bean
public LdapAuthenticator ldapAuthenticator() {
BindAuthenticator authenticator = new BindAuthenticator(ldapContextSource());
authenticator.setUserSearch(new FilterBasedLdapUserSearch("OU=company,OU=companygroup users,DC=int,DC=root,DC=company,DC=ag", "(sAMAccountName={0})", ldapContextSource()));
return authenticator;
}
}
Reference:
https://www.stevenschwenke.de/LDAPWithSpringSecurity

Spring Boot - Custom Filter/Stateless auth and #Secured annotation

I have been struggling with this for over 2 hours with no luck after reading around 10 different articles.
I want to use my custom filter to perform stateless authorization based on roles from DB and #Secured annotation.
Let's start with my example account identified in database by api-key: '6c1bb23e-e24c-41a5-8f12-72d3db0a6979'.
He has following String role fetched from DB: 'FREE_USER_ROLE'.
My filter:
public class ApiKeyAuthFilter extends OncePerRequestFilter {
private final AccountService accountService;
private final GlobalExceptionsAdvice exceptionAdvice;
private static final String API_KEY_HEADER_FIELD = "X-AUTH-KEY";
public static final List<String> NON_AUTH_END_POINTS
= Collections.unmodifiableList(Arrays.asList("/Accounts", "/Accounts/Login"));
AntPathMatcher pathMatcher = new AntPathMatcher();
public ApiKeyAuthFilter(AccountService accountService, GlobalExceptionsAdvice exceptionAdvice) {
this.accountService = accountService;
this.exceptionAdvice = exceptionAdvice;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain fc) throws ServletException, IOException {
Optional authKey = Optional.ofNullable(request.getHeader(API_KEY_HEADER_FIELD));
if (!authKey.isPresent()) {
sendForbiddenErrorMessage(response);
} else {
try {
AccountDTO account = accountService.findByApiKey(authKey.get().toString());
Set<GrantedAuthority> roles = new HashSet();
account.getRoles().forEach((singleRole) -> roles.add(new SimpleGrantedAuthority(singleRole.getName())));
Authentication accountAuth = new UsernamePasswordAuthenticationToken(account.getEmail(), account.getApiKey(),
roles);
SecurityContextHolder.getContext().setAuthentication(accountAuth);
SecurityContextHolder.getContext().getAuthentication().getAuthorities().forEach((role) -> {
System.out.println(role.getAuthority());
});
fc.doFilter(request, response);
} catch (ElementDoesNotExistException ex) {
//TODO: Add logging that user tried to falsy authenticate
sendForbiddenErrorMessage(response);
}
}
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return NON_AUTH_END_POINTS.stream().anyMatch(p -> {
return pathMatcher.match(p, request.getServletPath())
&& request.getMethod().equals("POST");
});
}
private void sendForbiddenErrorMessage(HttpServletResponse resp) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ErrorDetail error = exceptionAdvice.handleAccessDeniedException();
resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().write(mapper.writeValueAsString(error));
}
As You can see I am using X-AUTH-KEY header to retrieve provided apiKey, then I fetch info from Database based on that key and assign appropiate roles into SecurityContextHolder. Until that point everything works. I am sending poper apiKey, DB returns 'FREE_USER_ROLE'.
My #Configuration annotation class. (I bet something is wrong here but I can not tell what):
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class ApiKeySecurityConfiguration extends WebSecurityConfigurerAdapter {
AccountService accountService;
GlobalExceptionsAdvice exceptionAdvice;
#Autowired
public ApiKeySecurityConfiguration(AccountService accountService, GlobalExceptionsAdvice exceptionAdvice) {
this.accountService = accountService;
this.exceptionAdvice = exceptionAdvice;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.csrf().disable();
httpSecurity.authorizeRequests().anyRequest().authenticated();
httpSecurity.addFilterBefore(new ApiKeyAuthFilter(accountService, exceptionAdvice), UsernamePasswordAuthenticationFilter.class);
}
}
And final piece of puzzle - Controller that uses #Secured:
#RestController
#RequestMapping("/Accounts")
public class AccountsResource {
#Secured({"FREE_USER_ROLE"})
#PutMapping()
public boolean testMethod() {
return true;
}
}
I have tried with both 'FREE_USER_ROLE' and 'ROLE_FREE_USER_ROLE'. Everytime I get 403 Forbidden.
So I have spent some more time yesterday on that and I have managed to get it working with #PreAuthorize annotation. Posting code below because it may be useful to someone in future.
Filter:
#Component
public class ApiKeyAuthFilter extends OncePerRequestFilter {
private final AccountService accountService;
private final GlobalExceptionsAdvice exceptionAdvice;
private static final String API_KEY_HEADER_FIELD = "X-AUTH-KEY";
public static final List<String> NON_AUTH_END_POINTS
= Collections.unmodifiableList(Arrays.asList("/Accounts", "/Accounts/Login"));
AntPathMatcher pathMatcher = new AntPathMatcher();
#Autowired
public ApiKeyAuthFilter(AccountService accountService, GlobalExceptionsAdvice exceptionAdvice) {
this.accountService = accountService;
this.exceptionAdvice = exceptionAdvice;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain fc) throws ServletException, IOException {
Optional authKey = Optional.ofNullable(request.getHeader(API_KEY_HEADER_FIELD));
if (!authKey.isPresent()) {
sendForbiddenErrorMessage(response);
} else {
try {
AccountDTO account = accountService.findByApiKey(authKey.get().toString());
Set<GrantedAuthority> roles = new HashSet();
account.getRoles().forEach((singleRole) -> roles.add(new SimpleGrantedAuthority(singleRole.getName())));
Authentication accountAuth = new UsernamePasswordAuthenticationToken(account.getEmail(), account.getApiKey(),
roles);
SecurityContextHolder.getContext().setAuthentication(accountAuth);
SecurityContextHolder.getContext().getAuthentication().getAuthorities().forEach((role) -> {
System.out.println(role.getAuthority());
});
fc.doFilter(request, response);
} catch (ElementDoesNotExistException ex) {
//TODO: Add logging that user tried to falsy authenticate
sendForbiddenErrorMessage(response);
}
}
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return NON_AUTH_END_POINTS.stream().anyMatch(p -> {
return pathMatcher.match(p, request.getServletPath())
&& request.getMethod().equals("POST");
});
}
private void sendForbiddenErrorMessage(HttpServletResponse resp) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ErrorDetail error = exceptionAdvice.handleAccessDeniedException();
resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().write(mapper.writeValueAsString(error));
}
}
Configuration file:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApiKeySecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.
csrf().disable().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
Secured methods and methods allowed for anybody to use:
#RestController
#RequestMapping("/Accounts")
public class AccountsResource {
#PostMapping
#PreAuthorize("permitAll()")
public boolean forAll() {
return true;
}
#PutMapping()
#PreAuthorize("hasAuthority('FREE_USER_ROLE')")
public boolean testMethod() {
return true;
}
}

spring security OAuth2 - custom ClientDetailsService

I'm currently working on a spring app for an Oauth2 authentication but I got some issue implementing a custom ClientDetailsService.
I can't use the common inMemory ou jdbc clientDetailsService because clients information arn't stored in my app, I get them from an external webservice. But when I set a custom ClientDetailService I don't get the access_confirmation page anymore (I get a blank page).
To show you my issue I don't use my app but the the vanilla test from the official spring--security-oauth project spring-security-oauth
Here's the application code:
#SpringBootApplication
#EnableResourceServer
#RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#RequestMapping("/")
public String home() {
return "Hello World";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public String create(#RequestBody MultiValueMap<String, String> map) {
return "OK";
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()");
}
public ClientDetailsService clientDetailsService() {
return new ClientDetailsService() {
#Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
BaseClientDetails details = new BaseClientDetails();
details.setClientId(clientId);
details.setAuthorizedGrantTypes(Arrays.asList("authorization_code") );
details.setScope(Arrays.asList("read, trust"));
details.setResourceIds(Arrays.asList("oauth2-resource"));
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_CLIENT"));
details.setAuthorities(authorities);
return details;
}
};
} //*/
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.withClientDetails(clientDetailsService());
/*clients.inMemory()
.withClient("test")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT")
.scopes("read", "trust")
.resourceIds("oauth2-resource");
//*/
// #formatter:on
}
}
}
As you can see I add my custom clientDetailsService and change the ClientDetailsServiceconfigurer configuration to set it instead of the in memory clientDetailsService.
My problem is that when try to get my token I don't get my access_confirmation page anymore after I logged the user.
I found my problem, my definition of the scopes in my clientDetails was false. I had Arrays.asList("read, trust") instead of Arrays.asList("read", "trust")
Did I missed something? do I have to set my custom clientDetailsService somewhere else?
Try changing your ClientDetails impl like so:
public ClientDetailsService clientDetailsService() {
return new ClientDetailsService() {
#Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
BaseClientDetails details = new BaseClientDetails();
details.setClientId(clientId);
details.setAuthorizedGrantTypes(Arrays.asList("authorization_code") );
details.setScope(Arrays.asList("read, trust"));
details.setRegisteredRedirectUri(Collections.singleton("http://anywhere.com"));
details.setResourceIds(Arrays.asList("oauth2-resource"));
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_CLIENT"));
details.setAuthorities(authorities);
return details;
}
};
} //*/
I am trying to do something similar but I have some different issue. Hope you guys can help me here .
I have a class below
#Entity
#Table(name = "oauth_client_details")
public class CustomOAuthClientDetails extends BaseClientDetails{
/**
*
*/
private static final long serialVersionUID = -2792747663814219416L;
#Id
#Override
public String getClientId() {
return super.getClientId();
}
#Override
public String getClientSecret() {
return super.getClientSecret();
}
#Override
public Collection<GrantedAuthority> getAuthorities() {
return super.getAuthorities();
}
}
A repository class declared like below
Optional<CustomOAuthClientDetails> customOAuthClientDetails = authClientDetailsRepository.findByClientId(clientId);
When I try to do like below
details.setAuthorizedGrantTypes(customOAuthClientDetails.get().getAuthorizedGrantTypes());
I am getting below error
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Collection, at table: oauth_client_details, for columns: [org.hibernate.mapping.Column(authorities)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:499) ~[hibernate-core-5.4.26.Final.jar:5.4.26.Final]
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:466) ~[hibernate-core-5.4.26.Final.jar:5.4.26.Final]
at org.hibernate.mapping.Property.isValid(Property.java:227) ~[hibernate-core-5.4.26.Final.jar:5.4.26.Final]
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:624) ~[hibernate-core-5.4.26.Final.jar:5.4.26.Final]
at org.hibernate.mapping.RootClass.validate(RootClass.java:267) ~[hibernate-core-5.4.26.Final.jar:5.4.26.Final]
at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:354) ~[hibernate-core-5.4.26.Final.jar:5.4.26.Final]
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:465) ~[hibernate-core-5.4.26.Final.jar:5.4.26.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1259) ~[hibernate-core-5.4.26.Final.jar:5.4.26.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58) ~[spring-orm-5.3.2.jar:5.3.2]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.3.2.jar:5.3.2]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-5.3.2.jar:5.3.2]
... 21 common frames omitted

spring security oauth2 java config - Handler dispatch failed; nested exception is java.lang.StackOverflowError

Configured Spring Security with OAuth2 in java config, And client_credentails flow is working fine, but password flow is throwing Handler dispatch failed; nested exception is java.lang.StackOverflowError below is the log information
2016-10-10 23:19:08 DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]: org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowError
here is my configuration files:
AuthorizationServerConfiguration.java
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(
AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.jdbc(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
}
ResourceServerConfiguration.java
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private String resourceId = "rest-api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
// #formatter:off
resources.resourceId(resourceId);
// #formatter:on
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated();
// #formatter:on
}
}
WebApplicationInitializer.java
public class WebAppInitializer implements WebApplicationInitializer {
private Logger logger = LoggerFactory.getLogger(getClass());
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext=new AnnotationConfigWebApplicationContext();
rootContext.register(ApplicationRootConfig.class, HibernateConfig.class, AnnotationBasedSecurityConfig.class,
GlobalAuthenticationConfig.class, SecurityConfiguration.class, AuthorizationServerConfiguration.class, ResourceServerConfiguration.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
servletContext.setInitParameter("defaultHtmlEscape", "true");
FilterRegistration.Dynamic encodingFilter=servletContext.addFilter("encoding-filter", new CharacterEncodingFilter());
encodingFilter.setInitParameter("encoding", "UTF-8");
encodingFilter.setInitParameter("forceEncoding", "true");
encodingFilter.addMappingForServletNames(null, true, "/*");
FilterRegistration.Dynamic securityFilter = servletContext.addFilter("securityFilter",new DelegatingFilterProxy("springSecurityFilterChain"));
securityFilter.addMappingForUrlPatterns(null, false,"/*");
servletContext.addListener(new HttpSessionEventPublisher());
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
Set<String> mappingConflicts = dispatcher.addMapping("/");
if(!mappingConflicts.isEmpty()){
for (String map : mappingConflicts) {
logger.error("Mapping Conflict "+map);
}
throw new IllegalStateException("Dispatcher : cannot be mapped to '/' under Tomcat vesions <= 7.0.4");
}
rootContext.setServletContext(servletContext);
rootContext.refresh();
}
}
UserDetailsServiceImpl.java
#Service
#Transactional(readOnly=true)
public class UserDetailsServiceImpl implements UserDetailsService{
Logger logger = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
#Autowired
UserDAO userDAO;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("loadUserByUsername "+username);
Collection<User> user = userDAO.findByEmail(username);
return user.iterator().next();
}
}
SecurityConfiguration.java
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
oauth_client_details table
Full Stack Trace
2016-10-14 18:05:43 DEBUG o.s.o.h.HibernateTransactionManager - Creating new transaction with name [org.springframework.security.oauth2.provider.token.DefaultTokenServices.createAccessToken]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2016-10-14 18:05:43 DEBUG o.s.o.h.HibernateTransactionManager - Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList#1e09a738 updates=org.hibernate.engine.spi.ExecutableList#67d0f9f8 deletions=org.hibernate.engine.spi.ExecutableList#c60ee2d orphanRemovals=org.hibernate.engine.spi.ExecutableList#3bdd7e0f collectionCreations=org.hibernate.engine.spi.ExecutableList#683e2e2b collectionRemovals=org.hibernate.engine.spi.ExecutableList#84115ed collectionUpdates=org.hibernate.engine.spi.ExecutableList#3db934e collectionQueuedOps=org.hibernate.engine.spi.ExecutableList#5326b83c unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
2016-10-14 18:05:43 DEBUG o.s.o.h.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList#1e09a738 updates=org.hibernate.engine.spi.ExecutableList#67d0f9f8 deletions=org.hibernate.engine.spi.ExecutableList#c60ee2d orphanRemovals=org.hibernate.engine.spi.ExecutableList#3bdd7e0f collectionCreations=org.hibernate.engine.spi.ExecutableList#683e2e2b collectionRemovals=org.hibernate.engine.spi.ExecutableList#84115ed collectionUpdates=org.hibernate.engine.spi.ExecutableList#3db934e collectionQueuedOps=org.hibernate.engine.spi.ExecutableList#5326b83c unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
2016-10-14 18:05:43 DEBUG o.s.o.h.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [com.mchange.v2.c3p0.impl.NewProxyConnection#4a1e11db]
2016-10-14 18:05:44 DEBUG o.s.o.h.HibernateTransactionManager - Initiating transaction rollback
2016-10-14 18:05:44 DEBUG o.s.o.h.HibernateTransactionManager - Rolling back Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList#1e09a738 updates=org.hibernate.engine.spi.ExecutableList#67d0f9f8 deletions=org.hibernate.engine.spi.ExecutableList#c60ee2d orphanRemovals=org.hibernate.engine.spi.ExecutableList#3bdd7e0f collectionCreations=org.hibernate.engine.spi.ExecutableList#683e2e2b collectionRemovals=org.hibernate.engine.spi.ExecutableList#84115ed collectionUpdates=org.hibernate.engine.spi.ExecutableList#3db934e collectionQueuedOps=org.hibernate.engine.spi.ExecutableList#5326b83c unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
2016-10-14 18:05:44 DEBUG o.s.o.h.HibernateTransactionManager - Closing Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList#1e09a738 updates=org.hibernate.engine.spi.ExecutableList#67d0f9f8 deletions=org.hibernate.engine.spi.ExecutableList#c60ee2d orphanRemovals=org.hibernate.engine.spi.ExecutableList#3bdd7e0f collectionCreations=org.hibernate.engine.spi.ExecutableList#683e2e2b collectionRemovals=org.hibernate.engine.spi.ExecutableList#84115ed collectionUpdates=org.hibernate.engine.spi.ExecutableList#3db934e collectionQueuedOps=org.hibernate.engine.spi.ExecutableList#5326b83c unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] after transaction
2016-10-14 18:05:44 DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]: org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowError
2016-10-14 18:05:44 DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Invoking #ExceptionHandler method: public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.exceptions.OAuth2Exception> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.handleException(java.lang.Exception) throws java.lang.Exception
2016-10-14 18:05:44 INFO o.s.s.o.p.endpoint.TokenEndpoint - Handling error: NestedServletException, Handler dispatch failed; nested exception is java.lang.StackOverflowError
2016-10-14 18:05:44 DEBUG o.s.w.s.m.m.a.HttpEntityMethodProcessor - Written [error="server_error", error_description="Handler dispatch failed; nested exception is java.lang.StackOverflowError"] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#120db352]
2016-10-14 18:05:44 DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling
2016-10-14 18:05:44 DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request
2016-10-14 18:05:44 DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'delegatingApplicationListener'
2016-10-14 18:05:44 DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally
2016-10-14 18:05:44 DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
Am testing from postman by passing grant_type = "password", username="user", password="pass" in body with form-urlencoded and Authorization Basic Auth(client details).
User.java
#Entity
#Table(name="xt_user_profile")
public class User extends XtremandTimeStamp implements UserDetails{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="user_id_seq")
#SequenceGenerator(
name="user_id_seq",
sequenceName="user_id_seq",
allocationSize=1
)
#Column(name="user_id")
private Integer userId;
#Column(name="user_name")
private String userName;
#Column(name="email_id")
private String emailId;
#Column(name="password")
private String password;
#Column(name="firstname")
private String firstName;
#Column(name="lastname")
private String lastName;
#Column(name="status")
#org.hibernate.annotations.Type(type="com.xtremand.user.bom.UserStatusType")
private UserStatus userStatus;
private String country;
private String city;
private String zip;
#Column(name="headline")
private String headLine;
private String description;
#Column(name="googlelogin")
private String googleLogin;
#Column(name="linkedinlogin")
private String linkedinLogin;
#Column(name="twitterlogin")
private String twitterLogin;
#Column(name="facebooklogin")
private String facebookLogin;
#Column(name="datereg", columnDefinition="DATETIME")
#Temporal(TemporalType.TIMESTAMP)
private Date dateReg;
#Column(name="datelastedit", columnDefinition="DATETIME")
#Temporal(TemporalType.TIMESTAMP)
private Date dateLastEdit;
#Column(name="datelastlogin", columnDefinition="DATETIME")
#Temporal(TemporalType.TIMESTAMP)
private Date dateLastLogin;
#Column(name="datelastnav", columnDefinition="DATETIME")
#Temporal(TemporalType.TIMESTAMP)
private Date dateLastNav;
#Column(name="alias")
private String alias;
public enum UserStatus{
// UNAPPROVED,APPROVE,DECLINE,BLOCK,SUSPEND,DELETE
UAPPROVED("UnApproved"),
APPROVED("APPROVED"),
DECLINE("DECLINE"),
BLOCK("BLOCK"), SUSPEND("SUSPEND"), DELETE("DELETE");
protected String status;
private UserStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}
public void initialiseCommonFields(boolean isCreate, int updatedBy){
super.initialiseCommonFields(isCreate, updatedBy);
this.setDateLastEdit(new Date());
this.setDateLastLogin(new Date());
this.setDateLastNav(new Date());
this.setDateReg(new Date());
}
#ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#Fetch(FetchMode.SUBSELECT)
#JoinTable(name = "xt_user_role", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "role_id",
nullable = false, updatable = false) })
private Set<Role> roles = new HashSet<Role>(0);
#Fetch(FetchMode.SUBSELECT)
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserSubscription> userSubscriptions = new HashSet<UserSubscription>(0);
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public UserStatus getUserStatus() {
return userStatus;
}
public void setUserStatus(UserStatus userStatus) {
this.userStatus = userStatus;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
public String getHeadLine() {
return headLine;
}
public void setHeadLine(String headLine) {
this.headLine = headLine;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getGoogleLogin() {
return googleLogin;
}
public void setGoogleLogin(String googleLogin) {
this.googleLogin = googleLogin;
}
public String getLinkedinLogin() {
return linkedinLogin;
}
public void setLinkedinLogin(String linkedinLogin) {
this.linkedinLogin = linkedinLogin;
}
public String getTwitterLogin() {
return twitterLogin;
}
public void setTwitterLogin(String twitterLogin) {
this.twitterLogin = twitterLogin;
}
public String getFacebookLogin() {
return facebookLogin;
}
public void setFacebookLogin(String facebookLogin) {
this.facebookLogin = facebookLogin;
}
public Date getDateReg() {
return dateReg;
}
public void setDateReg(Date dateReg) {
this.dateReg = dateReg;
}
public Date getDateLastEdit() {
return dateLastEdit;
}
public void setDateLastEdit(Date dateLastEdit) {
this.dateLastEdit = dateLastEdit;
}
public Date getDateLastLogin() {
return dateLastLogin;
}
public void setDateLastLogin(Date dateLastLogin) {
this.dateLastLogin = dateLastLogin;
}
public Date getDateLastNav() {
return dateLastNav;
}
public void setDateLastNav(Date dateLastNav) {
this.dateLastNav = dateLastNav;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public Set<UserSubscription> getUserSubscriptions() {
return this.userSubscriptions;
}
public void setUserSubscriptions(Set<UserSubscription> userSubscriptions) {
this.userSubscriptions = userSubscriptions;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return getRoles();
}
#Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}
#Override
public String getUsername() {
return getUsername();
}
}
Please try to add next configuration, might help you:
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
This will allow both methods for authorization.
And add:
oauthServer.allowFormAuthenticationForClients() // it should allow authorize from form submitting.
Also check that you granted authorizedGrantTypes "password" for your clients in db.
In your User.class check method:
#Override
public String getUsername() {
return getUsername();
}
it's references on himself and when spring tries get username raise StackOverflowException. Please, change implementation of this method, it will fix your StackOverflowException
.
Please pay attention to the method name is authenticationManagerBean, rather than the authenticationManager
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

Resources