Spring Security : UserDetailsService works only once - spring

I seem to be missing something fundamental here:
#SpringBootApplication
public class Application {
User u = new User("USER", "PASSWORD",AuthorityUtils.createAuthorityList(
"ROLE_USER", "ROLE_ADMINISTRATOR"));
#Bean
public UserDetailsService userDetailsService() {
// returning a new User object works fine for every request
return username -> new User("USER", "PASSWORD",
AuthorityUtils.createAuthorityList(
"ROLE_USER", "ROLE_ADMINISTRATOR"));
// returning a previously created User object
// works only for the first request,
// subsequent requests get a 401 error
// return username -> u;
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
This Spring Boot (v1.5.1) application using the spring-boot-starter-security dependency knows of only one user as of now. Also, all of its endpoints should only be accessible to this very user. In all of the working examples I have seen, the UserDetailsService always returns a new object of type User, just like in the above example.
But when it returns a previously created object (like the object named u above), only the first request gets authenticated. Why ?

A good complete example, with JPA as well, can be found here
This is just an example. Password still needs to be encrypted/secured.
Application.java
package demo;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.data.repository.CrudRepository;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#ComponentScan
#EnableAutoConfiguration
#EnableGlobalMethodSecurity(securedEnabled = true)
public class Application extends WebMvcConfigurerAdapter {
#Controller
protected static class HomeController {
#RequestMapping("/")
#Secured("ROLE_ADMIN")
public String home(Map<String, Object> model) {
model.put("message", "Hello World");
model.put("title", "Hello Home");
model.put("date", new Date());
return "home";
}
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/access").setViewName("access");
}
#Bean
public ApplicationSecurity applicationSecurity() {
return new ApplicationSecurity();
}
#Order(Ordered.HIGHEST_PRECEDENCE)
#Configuration
protected static class AuthenticationSecurity extends
GlobalAuthenticationConfigurerAdapter {
#Autowired
private Users users;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(users);
}
}
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests().antMatchers("/login").permitAll().anyRequest()
.fullyAuthenticated().and().formLogin().loginPage("/login")
.failureUrl("/login?error").and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).and()
.exceptionHandling().accessDeniedPage("/access?error");
// #formatter:on
}
}
}
#Service
class Users implements UserDetailsService {
private UserRepository repo;
#Autowired
public Users(UserRepository repo) {
this.repo = repo;
}
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = repo.findByName(username);
if (user == null) {
return null;
}
List<GrantedAuthority> auth = AuthorityUtils
.commaSeparatedStringToAuthorityList("ROLE_USER");
if (username.equals("admin")) {
auth = AuthorityUtils
.commaSeparatedStringToAuthorityList("ROLE_ADMIN");
}
String password = user.getPassword();
return new org.springframework.security.core.userdetails.User(username, password,
auth);
}
}
#Repository
interface UserRepository extends CrudRepository<User, Long> {
User findByName(String name);
}
#Entity
class User {
#GeneratedValue
#Id
private Long id;
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

I believe it is due to the Spring User object purging the password after authentication
from -> https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/core/userdetails/User.html
Note that this implementation is not immutable. It implements the CredentialsContainer interface, in order to allow the password to be erased after authentication. This may cause side-effects if you are storing instances in-memory and reusing them. If so, make sure you return a copy from your UserDetailsService each time it is invoked.

Related

Spring Security #RolesAllowed not working [duplicate]

This question already has answers here:
Springboot Security hasRole not working
(3 answers)
Closed 4 months ago.
Here is my User Details Object
2022-10-19 07:35:58.143 INFO 24580 --- [nio-8081-exec-6] c.s.jwt.api.filter.JwtFilter : User Details: org.springframework.security.core.userdetails.User [Username=502553205, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[Authority(id=1, authority=Engine Line Tracker, description=Engine Line Tracker), Authority(id=2, authority=Pin a Card, description=Pin a Card), Authority(id=5, authority=Project Plan, description=Project Plan), Authority(id=4, authority=Scorecard, description=Scorecard), Authority(id=6, authority=Search, description=Search), Authority(id=3, authority=Upload/Download/Edit, description=Upload/Download/Edit)]]
My Endpoint is
#GetMapping
#RolesAllowed({"Scorecard"})
public List<User> list(){
return userRepository.findAll();
}
This runs when I remove #RolesAllowed but not taking roles from my user object.
Am I doing sth wrong in placing roles?
Yes its enabled
package com.springimplant.jwt.api.config;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.springimplant.jwt.api.filter.JwtFilter;
import com.springimplant.jwt.api.service.CustomUserDetailService;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
prePostEnabled = false,
securedEnabled = false,
jsr250Enabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailService userDetailService;
#Autowired
private JwtFilter filter;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean(name= BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/authenticate").permitAll()
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(((request, response, authException) -> {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
authException.getMessage();
} )).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(filter,UsernamePasswordAuthenticationFilter.class);
}
}
Also my Authority class is as follows
package com.springimplant.jwt.api.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "authorties")
public class Authority implements GrantedAuthority{
// private static final long serialVersionUID = 1L;
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
#Id
#SequenceGenerator(name = "authorties_seq", sequenceName = "authorties_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "authorties_seq")
private Long id;
#Column(name="authority")
private String authority;
#Column(name="description")
private String description;
#Override
public String getAuthority() {
return authority;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Authority) {
return this.authority.equals(((Authority) obj).authority);
}
return false;
}
#Override
public int hashCode() {
return this.authority.hashCode();
}
#Override
public String toString() {
return this.authority;
}
}
One gotcha is that #RolesAllowed will expect the user has the authority that start with the prefix ROLE_ by default. So #RolesAllowed({"Scorecard"}) will expect the user has the authority ROLE_Scorecard. But now since the user only has the authority Scorecard , it does not match ROLE_Scorecard and hence it is access denied.
The easy fix is change to use #PreAuthorize :
#GetMapping
#PreAuthorize("hasAuthority('Scorecard')")
public List<User> list(){
return userRepository.findAll();
}
It will directly check if the user has the authority Scorecard without any prefix behavior.
First of all, I believe it's a good practice to make role names short without spaces, slashes, and underscored. Your list may look like this: SCORECARD, PROJECT_PLAN, UPLOAD_DOWNLOAD_EDIT.
To use #RolesAllowed you have to make sure you enabled jsr250 in #EnableGlobalMethodSecurity:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {}
or for Spring 5.6 and later you can replace #EnableGlobalMethodSecurity annotation with #EnableMethodSecurity
This allows you to use #Secured, #PreAuthorized, #PostAuthorized and #RolesAllowed.
More info about it is in the official docs
===== UPD =====
Now, when you provided more details I can guess that you missed one important thing: role != authority. By default roles in Spring have ROLE_ prefix. If you didn't replace it anywhere it just tries to compare ROLE_Scorecard to Scorecard.
To remove that prefix you can do this in your config:
#Bean
fun grantedAuthorityDefaults(): GrantedAuthorityDefaults? {
return GrantedAuthorityDefaults("") // Remove the ROLE_ prefix
}
=== UPD2 ===
Ha, one more good point appeared from Ken Chan while I were writing the update. If you're going to use an authorities approach than Ken Chan's hasAuthority() fits even better.

Registration Issue with Spring Security 401 with Postman

I had problem in spring security when I try to register a new user and tested it in the postman it keep giving me a 401 unauthorized response.
I checked all the filters, control, service repository and everything I already checked all the issues here and even searched a lot about it in google but no answer I hope some one had the answer.
this is the code below:
this is the Security Configuration:
package app.gym.v1.Utility.Config;
import app.gym.v1.Utility.Filter.JwtAccessDeniedHandler;
import app.gym.v1.Utility.Filter.JwtAuthenticationEntryPoint;
import app.gym.v1.Utility.Filter.JwtAuthorizationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import static app.gym.v1.Utility.Constant.SecurityConstant.*;
import static org.springframework.security.config.http.SessionCreationPolicy.*;
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private JwtAuthorizationFilter jwtAuthorizationFilter;
private JwtAccessDeniedHandler jwtAccessDeniedHandler;
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public SecurityConfig(
JwtAuthorizationFilter jwtAuthorizationFilter,
JwtAccessDeniedHandler jwtAccessDeniedHandler,
JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
#Qualifier("userDetailsService")UserDetailsService userDetailsService,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.jwtAuthorizationFilter = jwtAuthorizationFilter;
this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().and()
.sessionManagement().sessionCreationPolicy(STATELESS)
.and().authorizeRequests().antMatchers(PUBLIC_URLS).permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().accessDeniedHandler(jwtAccessDeniedHandler)
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);
}
#Bean
#Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManagerBean();
}
}
this is the Resource code:
package app.gym.v1.Resource;
import app.gym.v1.Model.User;
import app.gym.v1.Service.UserService;
import app.gym.v1.Utility.Exception.Domain.*;
import app.gym.v1.Utility.Exception.ExceptionHandling;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import static org.springframework.http.HttpStatus.OK;
#RestController
#RequestMapping(path = {"/","/user"})
public class UserControl extends ExceptionHandling {
private UserService userService;
#Autowired
public UserControl(UserService userService) {
this.userService = userService;
}
#PostMapping("/register")
public ResponseEntity<User> register(#RequestBody User user) throws UserNotFoundException, UsernameExistException, EmailExistException, IOException {
User newUser = userService.register(user.getUsername(), user.getEmail(), user.getPassword(), user.getRole());
return new ResponseEntity<>(newUser, OK);
}
}
this is the user implementation service:
package app.gym.v1.Utility.Impl;
import app.gym.v1.Model.User;
import app.gym.v1.Model.UserPrincipal;
import app.gym.v1.Repo.UserRepo;
import app.gym.v1.Service.UserService;
import app.gym.v1.Utility.Exception.Domain.*;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.transaction.Transactional;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import static app.gym.v1.Utility.Constant.UserImplConstant.*;
import static app.gym.v1.Utility.Enums.Role.*;
import static org.apache.commons.lang3.StringUtils.*;
#Service
#Transactional
#Qualifier("UserDetailsService")
public class UserServiceImpl implements UserService, UserDetailsService {
private Logger LOGGER = LoggerFactory.getLogger(getClass());
private UserRepo userRepo;
private BCryptPasswordEncoder passwordEncoder;
#Autowired
public UserServiceImpl(UserRepo userRepo, BCryptPasswordEncoder passwordEncoder) {
this.userRepo = userRepo;
this.passwordEncoder = passwordEncoder;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepo.findUserByUsername(username);
if (user == null) {
LOGGER.error("User with this phone number does not exist: " + username);
throw new UsernameNotFoundException("User with this phone number does not exist: " + username);
}else {
user.setLastLoginDateDisplay(user.getLastLoginDate());
user.setLastLoginDate(new Date());
userRepo.save(user);
UserPrincipal userPrincipal = new UserPrincipal(user);
LOGGER.info("Retrieving user with this phone number" + username);
return userPrincipal;
}
}
#Override
public User register(String username, String email, String password, String role) throws UserNotFoundException, UsernameExistException, EmailExistException {
validateNewUsernameAndEmail(EMPTY, username, email);
User user = new User();
user.setUserId(generateUserId());
user.setUsername(username);
user.setEmail(email);
user.setPassword(encodePassword(password));
user.setRole(USER.name());
user.setAuthorities(USER.getAuthorities());
user.setJoinDate(new Date());
user.setActive(true);
user.setNotLocked(true);
userRepo.save(user);
return user;
}
private String encodePassword(String password) {
return passwordEncoder.encode(password);
}
private String generateUserId() {
return RandomStringUtils.randomNumeric(20);
}
private String generatePassword() {
return RandomStringUtils.randomAlphanumeric(20);
}
private User validateNewUsernameAndEmail(String currentUsername, String newUsername, String newEmail) throws UserNotFoundException, UsernameExistException, EmailExistException {
User userByNewUsername = findUserByUsername(newUsername);
User userByNewEmail = findUserByEmail(newEmail);
if(isNotBlank(currentUsername)) {
User currentUser = findUserByUsername(currentUsername);
if(currentUser == null) {
throw new UserNotFoundException(NO_USER_FOUND_BY_USERNAME + currentUsername);
}
if(userByNewUsername != null && !currentUser.getId().equals(userByNewUsername.getId())) {
throw new UsernameExistException(USERNAME_ALREADY_EXISTS);
}
if(userByNewEmail != null && !currentUser.getId().equals(userByNewEmail.getId())) {
throw new EmailExistException(EMAIL_ALREADY_EXISTS);
}
return currentUser;
} else {
if(userByNewUsername != null) {
throw new UsernameExistException(USERNAME_ALREADY_EXISTS);
}
if(userByNewEmail != null) {
throw new EmailExistException(EMAIL_ALREADY_EXISTS);
}
return null;
}
}
}
the problem is with the registration my route is that(localhost:8080/user/register) or (localhost:8080/register).
I put a constant for them to make a public urls.
You need to annotate your SecurityConfig class with #Configuration or it won't be picked up.
If you do not have a custom security configuration set up properly, the application will use the default Spring Boot autoconfiguration which restricts access to all endpoints.

#Autowired Repository in ConstraintValidator null when used from CommandLineRunner

I'm trying to set up a Spring-Data-Rest App using Spring Boot 1.5.9.RELEASE. I have a ConstraintValidator set up like this:
UniqueEmail.java:
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotNull;
import java.lang.annotation.*;
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = {UniqueEmailValidator.class})
#NotNull
#ReportAsSingleViolation
public #interface UniqueEmail {
Class<?>[] groups() default {};
String message() default "{eu.icarus.momca.backend.domain.validation.UniqueEmail.message}";
Class<? extends Payload>[] payload() default {};
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
static #interface List {
UniqueEmail[] value();
}
}
UniqueEmailValidator.java:
import eu.icarus.momca.backend.domain.repository.AccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
#Autowired
private AccountRepository accountRepository;
#Override
public void initialize(UniqueEmail annotation) {
}
#Override
public boolean isValid(String email, ConstraintValidatorContext context) {
return accountRepository.findByEmail(email) == null;
}
}
I've also set up listeners for the beforeCreate and beforeSave repository methods:
RestConfiguration.java:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
#Configuration
public class RestConfiguration extends RepositoryRestConfigurerAdapter {
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", localValidatorFactoryBean());
validatingListener.addValidator("beforeSave", localValidatorFactoryBean());
}
#Bean
#Primary
LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
}
My problem:
The validator seems to work when the repository methods are called through REST endpoints, however I also try to set up some stuff in the connected database via a CommandLineRunner that uses the same repository. When the calls to the repository are validated, the autowired AccountRepository in the ConstraintValidator is null even if it isn't null at the same time in the CommandLineRunner that also has the same repository autowired. I don't understand this.
Does anybody have an idea why the repository is null when the validation is triggered from the CommandLineRunner?
Any help is greatly appreciated.
Daniel
Edit: added the CommandLineRunner Code
InitAdminData.java:
import eu.icarus.momca.backend.domain.entity.Account;
import eu.icarus.momca.backend.domain.entity.AccountDetails;
import eu.icarus.momca.backend.domain.entity.AccountProfile;
import eu.icarus.momca.backend.domain.entity.Client;
import eu.icarus.momca.backend.domain.enumeration.AccountRoles;
import eu.icarus.momca.backend.domain.enumeration.ClientRoles;
import eu.icarus.momca.backend.domain.enumeration.GrantTypes;
import eu.icarus.momca.backend.domain.repository.AccountRepository;
import eu.icarus.momca.backend.domain.repository.ClientRepository;
import eu.icarus.momca.backend.domain.service.AccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
#Component
public class InitAdminData implements CommandLineRunner {
private static final Collection<AccountRoles> DEFAULT_ACCOUNT_ROLES = Arrays.asList(AccountRoles.ROLE_ACCOUNT, AccountRoles.ROLE_ADMINISTRATOR, AccountRoles.ROLE_CLIENT_OWNER);
private static final Logger logger = LoggerFactory.getLogger(InitAdminData.class);
private AccountRepository accountRepository;
private AccountService accountService;
#Value("${app.admin-email}")
private String adminEmail;
#Value("${app.admin-name}")
private String adminName;
#Value("${app.admin-password}")
private String adminPassword;
private ClientRepository clientRepository;
#Value("${app.default-client-description}")
private String defaultClientDescription;
#Autowired
public InitAdminData(AccountRepository accountRepository, AccountService accountService, ClientRepository clientRepository) {
this.accountRepository = accountRepository;
this.clientRepository = clientRepository;
this.accountService = accountService;
}
private Account initAdminAccount() {
Account adminAccount = accountRepository.findByAccountProfile_Name(adminName);
if (adminAccount == null) {
adminAccount = new Account();
logger.info("Initialized new admin account");
} else {
logger.info("Refreshed admin account");
}
setAdminAccountData(adminAccount);
return accountService.save(adminAccount, adminPassword);
}
private void initDefaultClient(Account adminAccount) {
long adminAccountId = adminAccount.getId();
Collection<Client> adminClients = clientRepository.findAllByOwnerId(adminAccountId);
if (adminClients.isEmpty()) {
Client defaultClient = new Client();
defaultClient.setOwnerId(adminAccountId);
defaultClient.setDescription(defaultClientDescription);
Collection<ClientRoles> clientRoles = new HashSet<>(2);
clientRoles.add(ClientRoles.ROLE_CLIENT);
clientRoles.add(ClientRoles.ROLE_DEFAULT_CLIENT);
defaultClient.setRoles(clientRoles);
Collection<GrantTypes> grantTypes = new HashSet<>();
grantTypes.add(GrantTypes.authorization_code);
grantTypes.add(GrantTypes.client_credentials);
grantTypes.add(GrantTypes.password);
grantTypes.add(GrantTypes.refresh_token);
defaultClient.setGrantTypes(grantTypes);
defaultClient = clientRepository.save(defaultClient);
logger.info(String.format("Added new default client with id '%s'", defaultClient.getId()));
}
}
#Override
public void run(String... args) {
Account adminAccount = initAdminAccount();
initDefaultClient(adminAccount);
}
private void setAdminAccountData(Account adminAccount) {
adminAccount.setEmail(adminEmail);
adminAccount.setRoles(DEFAULT_ACCOUNT_ROLES);
adminAccount.setAccountDetails(new AccountDetails());
adminAccount.setAccountProfile(new AccountProfile(adminName));
}
}
Add the #ComponentScan annotation.
#ComponentScan
public class InitAdminData implements CommandLineRunner {
...
}
I think you may have few cases :-
you haven't autowired your service and Repo
you haven't scan your Repo where you have your service and Repo.
Or you haven't use #Component(any stereotype) annotation on your classes which you are autowiring

Missing grant type error

I am just trying to learn OAuth. I wrote some code to test it out. when I am submitting a request I am getting
{
"error": "invalid_request",
"error_description": "Missing grant type"
}
error in postman.
import java.util.Optional;
//import static org.assertj.core.api.Assertions.tuple;
import java.util.stream.Stream;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.springframework.security.core.userdetails.User;
//import org.omg.PortableInterceptor.ACTIVE;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.stereotype.Service;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#SpringBootApplication
public class SpringAuthServiceApplication {
#Bean
CommandLineRunner clr(AccountRepository accountRepository){
return args -> {
Stream.of("name1, password1", "name2, password2", "name3, password3", "name4, password4")
.map(tpl -> tpl.split(",") )
.forEach(tpl -> accountRepository.save(new Account(tpl[0], tpl[1], true)));
};
}
public static void main(String[] args) {
SpringApplication.run(SpringAuthServiceApplication.class, args);
}
}
#Configuration
#EnableAuthorizationServer
class AuthServiceConfiguration extends AuthorizationServerConfigurerAdapter{
private final AuthenticationManager authenticationManager;
public AuthServiceConfiguration(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("html5")
.secret("password")
.authorizedGrantTypes("password")
.scopes("openid");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(this.authenticationManager);
}
}
#Service
class AccountUserDetailService implements UserDetailsService{
private final AccountRepository accountRepository;
public AccountUserDetailService(AccountRepository accountRepository) {
// super();
this.accountRepository = accountRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
return accountRepository.findByUsername (username)
.map(account -> new User(account.getUsername(),
account.getPassword(), account.isActive(), account.isActive(), account.isActive(), account.isActive(),
AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER") )
)
.orElseThrow(() -> new UsernameNotFoundException("Couldn't fine user name " + username + "!") ) ;
}
/*#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return accountRepository.findByUsername(username)
.map(account -> {
boolean active = account.isActive();
return new User(
account.getUsername(),
account.getPassword(),
active, active, active, active,
AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER"));
})
.orElseThrow(() -> new UsernameNotFoundException(String.format("username %s not found!", username)));
}*/
}
interface AccountRepository extends JpaRepository<Account, Long>{
Optional<Account> findByUsername(String username);
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
class Account{
public Account(String username, String password, boolean active) {
//super();
this.username = username;
this.password = password;
this.active = active;
}
#GeneratedValue #Id
private long id;
private String username, password;
private boolean active;
}
Here is what I am sending in postman:
In the header tab:
Content-Type: application/json
Authorization: Basic aHRtbDU6cGFzc3dvcmQ=
In the Authorization tab:
type is Basic Auth
Username: html5
Password: password
Body tab, selected form data and sending the following:
username: username
password: password1
grant_type: password
scope: openid
client_id: html5
client_secret: password
OAuth2 is looking for parameters in the form of a query string inside the request body, aka application/x-www-form-urlencoded.
Change your Content-Type to application/x-www-form-urlencoded and check x-www-form-urlencoded instead of form-data.

Can't integrate Spring Boot Data with Neo4j

I just started with Spring Boot Data Neo4j and trying to finish the movie tutorial. I got this ERROR but not really sure how to debug. Error summary : (1) Error creating bean (2)Could not autowire field. Any help, thank you.
My files structure as follows :
SpringBootApplication.java
package com.test.springdataneothree;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.test.services.MovieService;
#EnableAutoConfiguration
#SpringBootApplication
public class SpringdataneothreeApplication implements CommandLineRunner {
#Autowired
MovieService movieService;
public static void main(String[] args) {
SpringApplication.run(SpringdataneothreeApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
System.out.println("Main Spring Boot Class");
movieService.countMovie();
}
}
Movie.java
package com.test.movie;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
#NodeEntity(label ="Movie")
public class Movie {
#GraphId
private Long id;
String title;
int released;
String tagline;
public Movie() { }
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getReleased() {
return released;
}
public void setReleased(int released) {
this.released = released;
}
public String getTagline() {
return tagline;
}
public void setTagline(String tagline) {
this.tagline = tagline;
}
}
MovieService.java
package com.test.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.test.repositories.MovieRepository;
#Service("MovieService")
public class MovieService {
#Autowired
MovieRepository movieRepository;
public void countMovie() {
movieRepository.count();
}
}
MovieRepository.java
package com.test.repositories;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.GraphRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.test.movie.Movie;
#Repository
public interface MovieRepository extends GraphRepository<Movie> {
Movie findById(long id);
Movie findByTitle(#Param("title") String title);
#Query("MATCH (m:Movie) WHERE m.title =~ ('(?i).*'+{title}+'.*') RETURN m")
Collection<Movie> findByTitleContaining(#Param("title") String title);
#Query("MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) RETURN m.title as movie, collect(a.name) as cast LIMIT {limit}")
List<Map<String,Object>> graph(#Param("limit") int limit);
}
MyNeo4jConfiguration.java
package com.test.configuration;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#EnableTransactionManagement
#EnableScheduling
#ComponentScan(basePackages = {"com.test"})
#Configuration
#EnableNeo4jRepositories(basePackages = "com.test.repositories")
public class MyNeo4jConfiguration extends Neo4jConfiguration {
#Override
public SessionFactory getSessionFactory() {
return new SessionFactory("com.test.movie");
}
}
You have a problem with auto wiring: the annotation on the MovieService is wrong, it should be:
#Service("movieService")
or just
#Service
You are trying to inject a bean with the capitalised name, and the context can't find it (the injection is done by name). Another solution is to use the #Qualifier annotation on the auto wired field, like
#Autowired
#Qualifier("MovieService")
MovieService movieService;

Resources