Trying to move the example from Spring documentation one step further, I would like to bind instances of a model "Bank Account" to instances of in-memory users. The intention is that two different users should see only their own account balance after logging in.
The documentation is here: https://spring.io/guides/gs/securing-web/
The user is built using the UserDetailsService
#Bean
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
// (*) <-- I guess here I would somehow
// bind an instance of `savingsAccount`.
return new InMemoryUserDetailsManager(user);
}
For testing purposes, I'd like to bind an object of a simple account model class to such a user instance. The following shows what this class would look like in a persistence-oriented approach. I don't know how to define it as an in-memory only type.
import javax.persistence.Entity;
import javax.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
public class SavingsAccount {
#Id
private int id;
// In cents.
private long savings;
}
Thanks for any advice.
Why dont you use your own class like this:
#Entity
#Table(name = "user")
#Data
#ToString
public class User implements UserDetails {
//Have your bankaccount instance mapping here
}
Related
I'm practicing making web pages using spring boot.
I created an h2 DB and connected it, and I want to show the name properties of the members table as a list on my web page.
I created the findall() method, but only an empty list is returned. What's wrong with my code?
my web page
MemberRepository
package com.example.testproject.store.h2.repository;
import com.example.testproject.store.h2.domain.Members;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface MemberRepository extends JpaRepository<Members, Integer> {
}
MemberService
package com.example.testproject.store.h2.service;
import com.example.testproject.store.h2.domain.Members;
import java.util.List;
public interface MemberService {
List<Members> getAll();
}
MemberServiceImpl
package com.example.testproject.store.h2.service;
import com.example.testproject.store.h2.domain.Members;
import com.example.testproject.store.h2.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.lang.reflect.Member;
import java.util.List;
#Service(value = "memberServiceImpl")
public class MemberServiceImpl implements MemberService {
#Autowired
private MemberRepository memberRepository;
public List<Members> getAll(){
return memberRepository.findAll();
}
}
MemberController
package com.example.testproject.controller;
import com.example.testproject.store.h2.domain.Members;
import com.example.testproject.store.h2.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
public class MemberController {
#Autowired
private MemberService memberService;
#GetMapping(value = "/members")
public List<Members> getAll() {
List<Members> users = memberService.getAll();
return users;
}
/*#GetMapping(value = "/members")
public List<Members> getAll() throws Exception {
return memberService.getAll();
}*/
}
Members(Entity)
package com.example.testproject.store.h2.domain;
import jakarta.persistence.*;
#Entity
#Table(name = "members")
public class Members {
#Id
private int id;
#Column
private String name;
}
I want to show the name properties of the members table as a list on my web page.
You have missed the getter setter method in entity class either you need to 1:-define getter setter method for each field or
2:-you need to add lombook dependency in pom.xml file and add #Data annootation on top of entity class so that when spring uses your entity can set and get the value from database and if you dontot want to give id explicitly then add #GeneratedValue(strategy=GenerationType.AUTO) so that spring automatically increase you id
I think you missed getters and setters in your Entity
Class just add them by using #Data annotation and try again.
You missed getters and setters for the fields in your Entity
You do not need any library, you can write it by yourself.
Otherwise, even if you use lombok, you should avoid to use #Data annotation on Entity, because it could lead to errors with default implementation of equals and hashCode. Instead you can use annotations #Getter and #Setter on the class.
I'm trying to get the username from the session scoped bean once the user has logged in using /users/login
I've Autowired a session scoped bean into a RestController and in one of the endpoints in the rest controller, I'm invoking a setter on the session scoped bean. But the effect of setters is not visible for requests from the same session.
The following is my session scoped bean:
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
#Getter
#Setter
public class SessionSpecificUserDetails implements Serializable {
private String userName;
}
import lombok.Getter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.annotation.SessionScope;
#Getter
#Configuration
public class UserSessionDetailsConfiguration {
#Bean
#SessionScope
public SessionSpecificUserDetails sessionSpecificUserDetails() {
return new SessionSpecificUserDetails();
}
}
The following is the RestController
import com.course.backend.coursebackend.config.SessionSpecificUserDetails;
import com.course.backend.coursebackend.dao.User;
import com.course.backend.coursebackend.repository.UserRepository;
import com.course.backend.coursebackend.utils.ResponseUtils;
import com.course.backend.coursebackend.utils.UserUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
UserRepository userRepository;
#Autowired
SessionSpecificUserDetails sessionSpecificUserDetails;
#GetMapping("/getLoggedInUser")
public String getLoggedInUser() {
return sessionSpecificUserDetails.getUserName();
}
#PostMapping("/login")
public ResponseUtils.CustomResponse login(#RequestBody User user) {
List<User> users = getAllUsers();
Optional<User> optionalUser = users.stream()
.filter(currentUser -> currentUser.getUserName().equals(user.getUserName()))
.findAny();
if (optionalUser.isEmpty()) {
return ResponseUtils.getErrorResponse(List.of("Username not found!"));
}
if (optionalUser.get().getPassword().equals(user.getPassword())) {
sessionSpecificUserDetails.setUserName(user.getUserName());
return ResponseUtils.getSuccessResponse("Login Successful!");
}
return ResponseUtils.getErrorResponse(List.of("Login failed due to wrong password!"));
}
}
I see that spring is creating proxy for the session scoped bean. (And may be because of that my setters are not having any effect even for the same session?)
My question is what's the correct way to use the session scoped beans in the RestController? And what's the good way to get the username for the same session across requests?
I tried marking UserController also as #SessionScope but that's also not working.
I am authenticating an application with a login page using Spring Seurity and Spring Boot. I am storing user credentials in a properties file (until we finalize a Database) with password in encrypted for starting 5 users.
Here is my properties file
User.properties
my.web.user[0].username=John
my.web.user[0].password=$2y$12$V
my.web.user[0].role=ADMIN,USER
my.web.user[1].username=Johny
my.web.user[1].password=$2y$12$5C
my.web.user[1].role=ADMIN,USER
my.web.user[2].username=McCain
my.web.user[2].password=$2y$12$ERL8mf5.
my.web.user[2].role=USER
So now we can add as many users we want but in this array format. Now, I am loading this properties file in Spring Boot.
MyWebApplication.java
#SpringBootApplication
#ComponentScan(basePackages = { "com.myorg.module" })
#EnableConfigurationProperties(UserConfig.class)
public class MyWebApplication {
public static void main(String[] args) {
ApplicationContext appCtxt = SpringApplication.run(MyWebApplication.class, args);
}
}
UserConfig.java
#Configuration
#PropertySource("user.properties")
#ConfigurationProperties(prefix="my.web")
public class UserConfig {
private List<User> user;
/**
* #return the user
*/
public List<User> getUser() {
return user;
}
/**
* #param user the user to set
*/
public void setUser(List<User> user) {
this.user = user;
}
}
UserDetailsServiceImpl
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User.UserBuilder;
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.Component;
import UserConfig;
import User;
#Component
public class UserDetailsServiceImpl implements UserDetailsService{
#Autowired
private UserConfig userConfig;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = findUserByUsername(username);
UserBuilder userBuilder = null;
if(user != null) {
userBuilder = org.springframework.security.core.userdetails.User.withUsername(username);
userBuilder.password(new BCryptPasswordEncoder().encode(user.getPassword()));
userBuilder.roles(user.getRoles());
}else {
throw new UsernameNotFoundException("User Not Found");
}
return userBuilder.build();
}
private User findUserByUsername(String username) {
System.out.println(userConfig);
return userConfig.getUser().get(0);
}
}
Now when I am loading the UserConfig into my UserDetailsServiceImpl using #Autowired. In this case, I am getting userConfig as null. However, when I am putting debug points into getter/setter in UserConfig. I can see that it is populating object with properties contents on Container startup. But later on it vanishes and make it NULL.
Can anyone let me know where am I making mistake?
Thanks in advance.
There are couple of learnings which I had on this.
WebSecurityConfigurerAdapter subclass - This child class should have #Autowired the UserDetailsService interface. In my case, I wrote UserDetailsServiceImpl which is the implementation of UserDetailsService interface.
Properties' array key name must match with the variable name of the #Configuration class. In my case, in properties, I had
my.web.user[0].username=John
and the variable name must be the same in #Configuration class like
#Configuration
#ConfigurationProperties(prefix="my.web")
#PropertySource("user.properties")
public class UserConfig {
private List<User> **user**;
And then it suddenly started working.
P.S. - I did lot of googling and research but nothing worked. Then I read this document 3 times to get it worked.
I t tried use new lastes version of Spring Hateoas 1.0 in my Spring Project, in this project all entities classes inherit from an Abstract Class, but in the Hateoas documentation my Entities must be extend the RepresentationModel Class
https://docs.spring.io/spring-hateoas/docs/1.0.1.RELEASE/reference/html/#fundamentals.representation-models
I've problems to extend the RepresentationModel on my Entity parent Class , somebody can help me please.
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import org.springframework.hateoas.RepresentationModel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#MappedSuperclass
#Getter #Setter #NoArgsConstructor
public abstract class Entity<T> extends RepresentationModel<T> implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private T id;
#Column
private Date createAt;
#Column
private Date updateAt;
}
I didn't use generic classes, but instead cast the whole object back to my concrete class in my controller.
Here is my super class
public class AbstractEntity extends RepresentationModel<AbstractEntity> implements Serializable {
}
Now my class extends the AbstractEntity:
public class User extends AbstractEntity {}
And in the controller we can have a method for adding the selfRef:
private User addSelfLink(User user) {
return (User) user.add(linkTo(methodOn(UserController.class).getById(user.getId())).withSelfRel());
}
All we have to do is cast the object bask to the final object we are using
I have a basic Interface which another class is implementing.
package info;
import org.springframework.stereotype.Service;
public interface Student
{
public String getStudentID();
}
`
package info;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
#Service
public class StudentImpl implements Student
{
#Override
public String getStudentID()
{
return "Unimplemented";
}
}
I then have a service to inject that class into
package info;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
#Service
public class InfoService {
#Autowired
Student student;
public String runProg()
{
return student.getStudentID();
}
}
What I want to know is, how do I set up a JUnit test so that a Mock class of the Student interface steps in with a stubbed method instead of the method in StudentImpl. The injection does work but I want to use amock class to simulate the results instead for the sake of testing. Any help would be greatly appreciated.
In my opinion, autowiring in unit tests is a sign that's it's an integration test rather than unit test, so I prefer to do my own "wiring", as you describe. It might require you to do some refactoring of your code, but it shouldn't be a problem. In your case, I would add a constructor to InfoService that get's a Student implementation. If you wish, you can also make this constructor #Autowired, and remove the #Autowired from the student field. Spring would then still be able to autowire it, and it's also more testable.
#Service
public class InfoService {
Student student;
#Autowired
public InfoService(Student student) {
this.student = student;
}
}
Then it will be trivial to pass mocks between your services in your tests:
#Test
public void myTest() {
Student mockStudent = mock(Student.class);
InfoService service = new InfoService(mockStudent);
}