HTTP Status 406 in rest api spring boot when getByEmail - java

I want to search by email but always get "error": "Not Acceptable",
#RestController
#RequestMapping("api/users")
public class UserController {
private final UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping(value = "/{name:.+}")
public User getUser(#PathVariable String name) {
return userService.getUserByEmail(name);
}
#Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public User getUserByEmail(String email){
User user = userRepository.findByEmail(email).get();
return user;
}
#Repository
public interface UserRepository extends JpaRepository<User,Long> {
Optional<User> findByEmail(#Param("email") String email);
}
even It can fetch from database but when want to return throw error
but throw error
add header application/json header but don't work.
another thing that I can get byId and firstName ,this work correctly

Try adding, value in pathVariable in the controller:
The content in bracket is a regex so it should work.
#GetMapping("/statusByEmail/{email:.+}/")
public String statusByEmail(#PathVariable(value = "email") String email){
//code
}
And from the postman/rest-client
Get http://mywebhook.com/statusByEmail/abc.test#gmail.com/
If this doesn't work try to give the email in URLEncoded format:
The problem might be due to the multiple . in the request
Eg: alireza.ca%40gmail.com
OR
You can set Content-Type: application/x-www-form-urlencoded to automatically do the encoding of the url
Hopefully, this should work.

Related

Spring NullPointerException this.service is null

I have problem with NullPointerException, every POST on /register endpoint, the NullPointerException shows that service is null
Controller with /register
#AllArgsConstructor
#RestController
#Validated
public class SecurityController {
private final UserService service;
#PostMapping("/register")
private ResponseEntity<UserDTO> registerUser (#RequestBody #Valid RegisterDTO registerDTO) {
return ResponseEntity.created(URI.create("/user")).body(service.createUser(registerDTO));
}
UserService
#RequiredArgsConstructor
#Service
public class UserService implements UserServiceApi {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final PasswordEncoder encoder;
#Override
public UserDTO createUser(RegisterDTO user) {
if (user.equals(userRepository.findByUsername(user.getUsername()))) {
throw new RuntimeException("This nickname is already taken");
}
if (user.equals(userRepository.findByEmail(user.getEmail()))) {
throw new RuntimeException("This email is already taken");
}
// Encoding password
user.setPassword(encoder.encode(user.getPassword()));
// On creating new Account it's going to have USER role
Role role = roleRepository.findByName("USER");
String username = user.getUsername();
String password = user.getPassword();
String email = user.getEmail();
User dto = buildUser(username, password, email, role);
userRepository.save(dto);
return UserDTO.builder()
.username(username)
.password(password)
.email(email)
.build();
}
Other controller that use service, I dont know maybe that's the cuase of problem
#RestController(value = "/user")
#AllArgsConstructor
#Validated
public class UserController {
private final UserService service;
#GetMapping("/getusers")
public ResponseEntity<List<User>> getAllUser() {
return ResponseEntity.ok(service.getUsers());
}

Hibernate: Why am I getting an empty json?

I have a simple entity class:
#Entity(name = "User")
#Table(name = "users")
#Data
#NoArgsConstructor
public class User {
#Id
#GeneratedValue
private Long id;
private String name;
private Integer age;
private String email;
}
A repository:
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
A service:
#Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public Iterable<User> findAllUsers() {
return userRepository.findAll();
}
public <U extends User> void saveUser(U user) {
userRepository.save(user);
}
}
A controller:
#RestController
#RequestMapping("api/v1/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping
public Iterable<User> getAllUsers() {
return userService.findAllUsers();
}
#PostMapping
public void addUser(#RequestBody User user) {
userService.saveUser(user);
}
}
And a yaml file:
spring:
datasource:
username: "root"
password: "123123"
url: "jdbc:mysql://localhost:3306/mydbtest"
driverClassName: "com.mysql.jdbc.Driver"
jpa:
hibernate.ddl-auto: update
generate-ddl: true
show-sql: true
I use MYSQL Workbench, where I have a database with one table called users. Sending a get request and trying to get all the objects from the table, I get an empty json. What should I do?
Your code seems ok to me . I think its your IDE issue, you have to enable Lombok annotation processing in your IDE. You can follow this link to enable it.
Could you add #AllArgsConstructor annotation to your entity. I think it might solve your issue. Since currently you only have #NoArgsConstructor and #RequiredArgsConstructor which comes bundled with #Data. And also you don't necessarily need the name paramenter in #Entity

What is the best practice to salt a password with spring security in spring boot?

I'm creating a REST API in java for an online store with Spring Boot, I want to securely store user passwords in the database,
for this I am using BCrypt that comes included with spring security, I use MySQL and JPA-Hibernate for persistence.
And I am implementing it as follows:
This is the user entity:
#Entity
#SelectBeforeUpdate
#DynamicUpdate
#Table (name = "USER")
public class User {
#Id
#GeneratedValue
#Column(name = "USER_ID")
private Long userId;
#Column(name = "ALIAS")
private String alias;
#Column(name = "NAME")
private String name;
#Column(name = "LAST_NAME")
private String lastName;
#Column(name = "TYPE")
private String type;
#Column(name = "PASSWORD")
private String password;
public String getPassword() {
return password;
}
/**
* When adding the password to the user class the setter asks if it is necessary or not to add the salt,
* if this is necessary the method uses the method BCrypt.hashpw (password, salt),
* if it is not necessary to add the salt the string That arrives is added intact
*/
public void setPassword(String password, boolean salt) {
if (salt) {
this.password = BCrypt.hashpw(password, BCrypt.gensalt());
} else {
this.password = password;
}
}
//Setters and Getters and etc.
}
This is the repository of the user class:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
This is the service of the user class:
#Service
public class UserService{
private UserRepository userRepository;
#Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User addEntity(User user) {
//Here we tell the password setter to generate the salt
user.setPassword(user.getPassword(), true);
return userRepository.save(user);
}
public User updateEntity(User user) {
User oldUser = userRepository.findOne(user.getUserId());
/*
*This step is necessary to maintain the same password since if we do not do this
*in the database a null is generated in the password field,
*this happens since the JSON that arrives from the client application does not
*contain the password field, This is because to carry out the modification of
*the password a different procedure has to be performed
*/
user.setPassword(oldUser.getPassword(), false);
return userRepository.save(user);
}
/**
* By means of this method I verify if the password provided by the client application
* is the same as the password that is stored in the database which is already saved with the salt,
* returning a true or false boolean depending on the case
*/
public boolean isPassword(Object password, Long id) {
User user = userRepository.findOne(id);
//To not create an entity that only has a field that says password, I perform this mapping operation
String stringPassword = (String)((Map)password).get("password");
//This method generates boolean
return BCrypt.checkpw(stringPassword, user.getPassword());
}
/**
*This method is used to update the password in the database
*/
public boolean updatePassword(Object passwords, Long id) {
User user = userRepository.findOne(id);
//Here it receive a JSON with two parameters old password and new password, which are transformed into strings
String oldPassword = (String)((Map)passwords).get("oldPassword");
String newPassword = (String)((Map)passwords).get("newPassword");
if (BCrypt.checkpw(oldPassword, user.getPassword())){
//If the old password is the same as the one currently stored in the database then the new password is updated
//in the database for this a new salt is generated
user.setPassword(newPassword, true);
//We use the update method, passing the selected user
updateEntity(user);
//We return a true boolean
return true;
}else {
//If the old password check fails then we return a false boolean
return false;
}
}
//CRUD basic methods omitted because it has no case for the question
}
This is the controller that exposes the API endpoints:
#RestController
#CrossOrigin
#RequestMapping("/api/users")
public class UserController implements{
UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#RequestMapping( value = "", method = RequestMethod.POST )
public User addEntity(#RequestBody User user) {
return userService.addEntity(user);
}
#RequestMapping( value = "", method = RequestMethod.PUT )
public User updateEntity(#RequestBody User user) {
return userService.updateEntity(user);
}
#RequestMapping( value = "/{id}/checkPassword", method = RequestMethod.POST )
public boolean isPassword(#PathVariable(value="id") Long id, #RequestBody Object password) {
return userService.isPassword(password, id);
}
#RequestMapping( value = "/{id}/updatePassword", method = RequestMethod.POST )
public boolean updatePassword(#PathVariable(value="id") Long id, #RequestBody Object password) {
return userService.updatePassword(password, id);
}
}
This is where my question comes, my method is working but I feel it is not the best way, I do not feel comfortable changing the password setter I would prefer to keep the standard form of a setter, as in the user service I think there is Opportunity to handle the user and password update differently, so try to use the #DynamicUpdate annotation in the entity but it simply does not work properly since the fields not provided in the update instead of leaving them as they were are saved Like nulls.
What I'm looking for is a better way to handle the security of passwords using Spring Boot.
First of all you would like to have a unique field for each user in your online store (f.e. alias, or email), to use it as an identifier, without exposing id value to the end users.
Also, as I understand, you want to use Spring Security to secure your web application. Spring security uses ROLEs to indicate user authorities (f.e. ROLE_USER, ROLE_ADMIN). So it would be nice to have a field (a list, a separate UserRole entity) to keep track of user roles.
Let's assume, that you added unique constraint to User field alias (private String alias;) and added simple private String role; field. Now you want to set up Spring Security to keep '/shop' and all sub-resources (f.e. '/shop/search') open to everyone, unsecured, resource '/discounts' available only for registered users and resource '/admin' available for administrator only.
To implement it, you need to define several classes. Let's start with implementation of UserDetailsService (needed by Spring Security to get user information):
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository repository;
#Autowired
public UserDetailsServiceImpl(UserRepository repository) {
this.repository = repository;
}
#Override
public UserDetails loadUserByUsername(String alias) {
User user = repository.findByAlias(alias);
if (user == null) {
//Do something about it :) AFAIK this method must not return null in any case, so an un-/ checked exception might be a good option
throw new RuntimeException(String.format("User, identified by '%s', not found", alias));
}
return new org.springframework.security.core.userdetails.User(
user.getAlias(), user.getPassword(),
AuthorityUtils.createAuthorityList(user.getRole()));
}
}
Then, the main class for configuring Spring Security is one, that extends WebSecurityConfigurerAdapter (the example was taken from the application with a form based authentication, but you can adjust it for your needs):
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/shop/**").permitAll()
.antMatchers("/discounts/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.usernameParameter("alias")
.passwordParameter("password")
.loginPage("/login").failureUrl("/login?error").defaultSuccessUrl("/")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "remember-me")
.logoutSuccessUrl("/")
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Then, in your UserService you can use something like:
...
#Autowired
private PasswordEncoder passwordEncoder;
public User addEntity(User user) {
...
user.setPassword(passwordEncoder.encode(user.getPassword()))
...
}
All other checks (f.e. for login attempt or for accessing resource) Spring Security will do automatically, according to the configuration. There are many more things to setup and consider, but I hope I was able to explain the overall idea.
EDIT
Define bean as follows within any spring Component or Configuration
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Then autowire it in your UserService class
#Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
#Autowired
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
public User addEntity(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword());
return userRepository.save(user);
}
...
public boolean isPassword(Object password, Long id) {
User user = userRepository.findOne(id);
String stringPassword = (String)((Map)password).get("password");
return passwordEncoder.matches(stringPassword, user.getPassword());
}
public boolean updatePassword(Object passwords, Long id) {
User user = userRepository.findOne(id);
String oldPassword = (String)((Map)passwords).get("oldPassword");
String newPassword = (String)((Map)passwords).get("newPassword");
if (!passwordEncoder.matches(oldPassword, newPassword)) {
return false;
}
user.setPassword(passwordEncoder.encode(newPassword));
updateEntity(user);
return true;
}
...
}
After that you can keep simple setter in User class.

SpringBoot JPA throws exception while making post request

Im making a small application where i can save user details using spring-boot. i created the entities and their corresponding repositories. When ever i make a post request to add a user the id of the user object which is null at the point of saving to the data base.This id is auto generated(Auto Increment) in MySQL. From the POST request i get 3 fields which are username,email,password. The User class contains fields id,username,email,password. I've added the annotations
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
for the id field. an the constructors are
public User() { }
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
This is the error im getting.
The debugging process
my userService class
#Service
public class UserService implements UserServiceInterface {
#Autowired(required = true)
private UserRepository userrepository;
#Override
public User CreateNewUser(User user) {
return userrepository.save(user);
}
}
my userController class
#RestController
public class UserController {
UserService us = new UserService();
#RequestMapping(value ="/user",method = RequestMethod.POST)
public void RegisterUser(
#RequestParam(value="username") String username,
#RequestParam(value="email") String email,
#RequestParam(value="password") String password){
us.CreateNewUser(new User(username,email,password));
}
}
Any reason why i cant POST to save data to database? how to overcome this?
After digging through the code i found out the error. by creating a new instance of UserService us = new UserService(); this is not managed by Spring (Spring doesn't know about it and cannot inject UserRepository - this causes NullPointerException). there of instead of creting new instace it should extends the UserService class in this example.

Authentication with Spring Security + Spring data + MongoDB

I want to use Spring security with MongoDB (using Spring data) and retrieve the users from my own database for spring security. However, I can not do that since my userservice type does not seem to be supported.
This is my UserService class:
public class UserService {
private ApplicationContext applicationContext;
private MongoOperations mongoOperations;
public UserService() {
applicationContext = new AnnotationConfigApplicationContext(MongoConfig.class);
mongoOperations = (MongoOperations) applicationContext.getBean("mongoTemplate");
}
public User find(String username) {
return mongoOperations.findOne(Query.query(Criteria.where("username").is(username)), User.class);
}
}
And my SecurityConfig class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Autowired
public void configAuthBuilder(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userService); //THIS DOES NOT WORK
builder.inMemoryAuthentication().withUser("username").password("password").roles("USER");
}
}
The line I commented says:
The inferred type UserService is not a valid substitute for the bounded parameter <T extends UserDetailsService>.
How can I fix it so I can retrieve the users from my own database?
Service Layer
You have to create a separate service implementing org.springframework.security.core.userdetails.UserDetailsService and inject it inside the AuthenticationManagerBuilder.
#Component
public class SecUserDetailsService implements UserDetailsService{
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/*Here add user data layer fetching from the MongoDB.
I have used userRepository*/
User user = userRepository.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException(username);
}else{
UserDetails details = new SecUserDetails(user);
return details;
}
}
}
Model
UserDetails Should be also implemented. This is the POJO which will keep the user authenticated details by the Spring. You may include your Entity data object wrapped inside it, as I have done.
public class SecUserDetails implements UserDetails {
private User user;
public SecUserDetails(User user) {
this.user = user;
}
......
......
......
}
Security Config
Autowire the service that we created before and set it inside the AuthenticationManagerBuilder
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
SecUserDetailsService userDetailsService ;
#Autowired
public void configAuthBuilder(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService);
}
}
Create your own authentication provider providing a class that extends the UserDetailservice.
Ensure content scanning is enable in your spring context xml file.
<authentication-provider user-service-ref="userModelService">
<password-encoder hash="sha" />
</authentication-provider>
#Service
public class UserModelService implements UserDetailsService
{
#Autowired
private UserModelRepositoryImpl repository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
UserModel user = repository.findByUsername(username);
if( user == null )
throw new UsernameNotFoundException( "Name not found!" );
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));
return new User(user.getUsername(), user.getSHA1Password(), authorities );
}
public void saveUserDetails(UserModel userModel)
{
repository.save(userModel);
}
}
This class will enable spring query mongo for the username and password required for authentication. Next create the user model class.
public class UserModel
{
private String id;
#Indexed(unique=true)
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
Create the user implementation class that extends the DAO.
#Service
public class UserModelService implements UserDetailsService
{
#Autowired
private UserModelRepositoryImpl repository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
UserModel user = repository.findByUsername(username);
if( user == null )
throw new UsernameNotFoundException( "Oops!" );
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));
return new User(user.getUsername(), user.getSHA1Password(), authorities );
}
Finally configure mongo and you're done.

Categories

Resources