I am making small web-store on Java for university project using Spring Boot and WebLogic server. I don`t use Hibernate, only JDBC.
In controller I need to get my current Spring Security User. I made CustomUser that implements UserDetails class and UserDetailsService implementation.
When I login into the server it accepts my credentials, page reloads, but immediately forwards me back to login page. It should redirect me just to /rootPath/ instead.
Here is my Security Config class
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Bean
public PasswordEncoder getEncoder(){
return NoOpPasswordEncoder.getInstance();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/registration").not().fullyAuthenticated()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/", "/store/**").hasAnyRole("ADMIN", "USER")
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
super.configure(auth);
}
}
Custom User implementation
public class CustomUser implements UserDetails {
private int id = -1;
private String name;
private String surname;
private String phone;
private String email;
private String password;
private String role;
private ProductCart productCart;
public CustomUser(int id, String name, String surname, String phone, String email, String password, String role) {
this.id = id;
this.name = name;
this.surname = surname;
this.phone = phone;
this.email = email;
this.password = password;
this.role = role;
}
public String getEmail() {
return email;
}
#Override
public Collection<GrantedAuthority> getAuthorities() {
GrantedAuthority authority = new SimpleGrantedAuthority(role);
return Collections.singletonList(authority);
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public String getRole() {
return role;
}
}
I tried to permit all requests to my /rootPath/ and get my User directly from Security Context and it showed me anonymousUser, when I login and was redirected to /rootPath/. I think that for some reason Spring or forgets my credentials, or just don't use my custom user as SecurityContext user and still use standart Spring user (for which I don't provide any service). I know for sure that my user loads from database correctly.
Here is my CustomUser class and UserDetailsService implementation (I removed getters and setters)
CustomUser.java
public class CustomUser implements UserDetails {
private int id = -1;
private String name;
private String surname;
private String phone;
private String email;
private String password;
private String role;
private ProductCart productCart;
public CustomUser(int id, String name, String surname, String phone, String email, String password, String role) {
this.id = id;
this.name = name;
this.surname = surname;
this.phone = phone;
this.email = email;
this.password = password;
this.role = role;
productCart = new ProductCart();
}
#Override
public Collection<GrantedAuthority> getAuthorities() {
GrantedAuthority authority = new SimpleGrantedAuthority(role);
return Collections.singletonList(authority);
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
UserDetailsServiceImpl.java
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
UserDao userDao;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
CustomUser customUser = userDao.getByEmail(email);
if (customUser == null) {
throw new UsernameNotFoundException("No user found with email: " + email);
}
return customUser;
}
}
Maybe I forgot to configure something or just my realization is wrong in some way?
You are auto-wiring the PasswordEncoder but where is the bean ? Spring is expecting a bean for auto-wiring of type PasswordEncoder
Specify the bean in your configuration file - SecurityConfig :
#Bean
public PasswordEncoder getEncoder(){
return NoOpPasswordEncoder.getInstance();
}
You have to populate the SecurityContextHolder with the principal information you have fetched. For instance, you can extend OncePerRequestFilter and add the following:
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
final SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
Related
I am trying to implement a database authentication using custom UserDetails implementation. I have three roles, which are STUDENT, ADMIN and ADMINTRAINEE (these are enums) and some authorities provided for them , which I fetch from in-memory db (but I'm gonna switch to external one). This is web security configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurityConfig {
private final PasswordEncoder passwordEncoder;
private final ApplicationUserService userService;
#Autowired
public ApplicationSecurityConfig(PasswordEncoder passwordEncoder,ApplicationUserService userService) {
this.passwordEncoder = passwordEncoder;
this.userService = userService;
}
#Bean
protected SecurityFilterChain filterChain(HttpSecurity http)
throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "index", "/css/*", "/js/*").permitAll()
.antMatchers("/api/**").hasRole(STUDENT.name())
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.defaultSuccessUrl("/courses", true)
.passwordParameter("password")
.usernameParameter("username")
.and()
.rememberMe()
.tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(21))
.key("example")
.rememberMeParameter("remember-me")
.and()
.logout()
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "remember-me")
.logoutSuccessUrl("/login"); // custom address to redirect after logout
return http.build();
}
// This is what I need to rewrite
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(daoAuthenticationProvider());
}
// Is used to utilize a custom impl of UserDetailsService
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder);
provider.setUserDetailsService(userService);
return provider;
}
}
This is what the implementation of the UserDetailsService looks like:
#Service
public class ApplicationUserService implements UserDetailsService {
private final ApplicationUserDao applicationUserDao;
#Autowired
public ApplicationUserService(#Qualifier("fake") ApplicationUserDao applicationUserDao) {
this.applicationUserDao = applicationUserDao;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return applicationUserDao
.selectApplicationUserByUsername(username)
.orElseThrow(() ->
new UsernameNotFoundException(String.format("Username %s not found", username)));
}
}
So it calls the selectApplicationUserByUsername() method which is here:
public interface ApplicationUserDao {
Optional<ApplicationUser> selectApplicationUserByUsername(String username);
}
And this is the implementation of that interface:
#Repository("fake")
public class FakeApplicationUserDaoService implements
ApplicationUserDao {
private final PasswordEncoder passwordEncoder;
#Autowired
public FakeApplicationUserDaoService(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
#Override
public Optional<ApplicationUser> selectApplicationUserByUsername(String username) {
return getApplicationUsers().stream()
.filter(applicationUser -> username.equals(applicationUser.getUsername()))
.findFirst();
}
private List<ApplicationUser> getApplicationUsers() {
List<ApplicationUser> applicationUsers = Lists.newArrayList(
new ApplicationUser(
"annasmith",
passwordEncoder.encode("password"),
STUDENT.getGrantedAuthorities(),
true,
true,
true,
true
),
new ApplicationUser(
"linda",
passwordEncoder.encode("password"),
ADMIN.getGrantedAuthorities(),
true,
true,
true,
true
),
new ApplicationUser(
"tom",
passwordEncoder.encode("password"),
ADMINTRAINEE.getGrantedAuthorities(),
true,
true,
true,
true
)
);
return applicationUsers;
}
}
And this is the ApplicationUser class which is a custom subsitute to the UserDetails default implementation class Spring Security uses:
public class ApplicationUser implements UserDetails {
private final Set<? extends GrantedAuthority> grantedAuthorities;
private final String password;
private final String username;
private final boolean isAccountNonExpired;
private final boolean isAccountNonLocked;
private final boolean isCredentialsNonExpired;
private final boolean isEnabled;
public ApplicationUser(String password,
String username,
Set<? extends GrantedAuthority> grantedAuthorities,
boolean isAccountNonExpired,
boolean isAccountNonLocked,
boolean isCredentialsNonExpired,
boolean isEnabled) {
this.grantedAuthorities = grantedAuthorities;
this.password = password;
this.username = username;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return grantedAuthorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return isAccountNonExpired;
}
#Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
}
#Override
public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
}
#Override
public boolean isEnabled() {
return isEnabled;
}
}
So these are the enums for roles and authorities I wrote (the purpose of these is only for creating roles and the authorities users have; STUDENT does not have any authorities):
public enum ApplicationUserRole {
STUDENT(Sets.newHashSet()), // Sets is a class from the external library Guava
ADMIN(Sets.newHashSet(COURSE_READ, COURSE_WRITE, STUDENT_READ, STUDENT_WRITE)),
ADMINTRAINEE(Sets.newHashSet(COURSE_READ, STUDENT_READ));
private final Set<ApplicationUserPermission> permissions;
ApplicationUserRole(Set<ApplicationUserPermission> permissions) {
this.permissions = permissions;
}
public Set<ApplicationUserPermission> getPermissions() {
return permissions;
}
public Set<SimpleGrantedAuthority> getGrantedAuthorities() {
Set<SimpleGrantedAuthority> permissions = getPermissions().stream()
.map(permission -> new SimpleGrantedAuthority(permission.getPermission()))
.collect(Collectors.toSet());
permissions.add(new SimpleGrantedAuthority("ROLE_" + this.name()));
return permissions;
}
}
This is the ApplicationUserPermission class:
public enum ApplicationUserPermission {
STUDENT_READ("student:read"),
STUDENT_WRITE("student:write"),
COURSE_READ("course:read"),
COURSE_WRITE("course:write");
private final String permission;
ApplicationUserPermission(String permission) {
this.permission = permission;
}
public String getPermission() {
return permission;
}
}
And PasswordConfig class:
#Configuration
public class PasswordConfig {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
}
The granted authorities then are used in a controller class for ADMIN and ADMINTRAINEE with the #PreAuthorize annotation.
So the problem I have is in ApplicationSecurityConfig. I don't know how to call the AuthenticationManangerBuilder to pass that daoAuthenticationProvider I have. In older versions of Spring Security, I could just override the configure method with AuthenticationManagerBuilder instance as an argument, but It's no longer the case since that abstract class is currently deprecated. So how do I rewrite the method? Or do I even have to do so? Pls any help is appreciated.
My mistake was about the order of credentials variables in ApplicationUser constructor.As it turned out it is very important.
What was the mistake:
public ApplicationUser(String password,
String username,
Set<? extends GrantedAuthority> grantedAuthorities,
boolean isAccountNonExpired,
boolean isAccountNonLocked,
boolean isCredentialsNonExpired,
boolean isEnabled) {
this.grantedAuthorities = grantedAuthorities;
this.password = password;
this.username = username;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
}
How it should be written:
public ApplicationUser(String username,
String password,
Set<? extends GrantedAuthority> grantedAuthorities,
boolean isAccountNonExpired,
boolean isAccountNonLocked,
boolean isCredentialsNonExpired,
boolean isEnabled) {
this.username = username;
this.password = password;
this.grantedAuthorities = grantedAuthorities;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
}
If you are using Spring Boot version with WebSecutiryConfigurerAdapter being deprecated, that configure method is not needed. You just build a securityFilterChain with the first method, and then provide password encoder and set userDetailsService for your db authentication.
I have been trying to add a create method to my spring boot security app but, when I use post mapping I get that error. Also, my id is auto-incremented in db. I am not sure but maybe the error is because of it. I don't know how to write an auto-incremented value in the request body.
{"timestamp":"2020-08-
23T00:43:31.062+00:00","status":403,"error":"Forbidden","message":"","path":"/createUser"}
The body that i am trying to post:
{
"id": 3,
"userName": "Adminn",
"password": "pss",
"active": true,
"role": "ROLE_ADMIN"
}
Request Body for Post Mapping
[1]: https://i.stack.imgur.com/uqoD0.png
My home resource class
package io.javabrains.springsecurity.jpa;
#RestController
public class HomeResource {
#Autowired
private UserRepository userRepo;
#GetMapping("/")
public String home() {
return ("<h1>Welcome</h1>");
}
#GetMapping("/user")
public String user() {
return ("Welcome User");
}
#GetMapping("/admin")
public String admin() {
return ("<h1>Welcome Admin</h1>");
}
#GetMapping("/users/{id}")
public Optional<User> retriveUser(#PathVariable int id)
{
return userRepo.findById(id);
}
#PostMapping("/createUser")
public void createUser(#RequestBody User myuser) {
User savedUser=userRepo.save(myuser);
}
/*#GetMapping("/createUser") // it is working
public String addUser() {
User newuser= new User();
newuser.setUserName("new");
newuser.setPassword(new BCryptPasswordEncoder().encode("pass"));
newuser.setRole("ROLE_ADMIN");
newuser.setActive(true);
userRepo.save(newuser);
return "user booked";
}*/
}
My Spring App Class
#SpringBootApplication
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
public class SpringsecurityApplication implements CommandLineRunner{
#Autowired
UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(SpringsecurityApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
System.out.println("Application Running.");
User adminUser= new User();
adminUser.setUserName("Admin");
adminUser.setPassword(new BCryptPasswordEncoder().encode("pass"));
adminUser.setRole("ROLE_ADMIN");
adminUser.setActive(true);
userRepository.save(adminUser);
User newUser= new User();
newUser.setUserName("User");
newUser.setPassword(new BCryptPasswordEncoder().encode("pass"));
newUser.setRole("ROLE_USER");
newUser.setActive(true);
userRepository.save(newUser);
}
}
User Class
package io.javabrains.springsecurity.jpa.models;
#Entity
#Table(name="app_user")
public class User {
#Id
#GeneratedValue(strategy =GenerationType.AUTO)
private int id;
private String userName;
private String password;
private boolean active;
private String role;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
Security Config Class
package io.javabrains.springsecurity.jpa;
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder ()
{
return new BCryptPasswordEncoder();
}
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasAnyRole("ADMIN")
.antMatchers("/user").hasAnyRole("ADMIN","USER")
.antMatchers("/","/createUser").permitAll()
.and().formLogin();
}
}
Because CSRF protection for state-changing HTTP verbs, such as POST, is enabled by default. You can either disable it or include CSRF token in your web page, and subsequently in your HTTP request.
What if you modify your code this way:
.antMatchers(HttpMethod.POST, "/createUser").permitAll()
First of all is a bad practice try to map the JSON request to an entity class. First you should use a DTO class.
Do that first and see what happend
I have integrated the role and want to manage the access of the specific service on the basis of the role.
An admin can create an AGENT and that agent came under the group of ADMIN user.
What I basically did have create a 1 to Many relation ship because my user could have only 1 role.
#Entity
#Table(name = "role")
public class Role {
private Long id;
private String name;
private Collection<User> users;
public Role() {
}
public Role(String name) {
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "role", cascade = CascadeType.ALL)
public Collection<User> getUsers() {
return users;
}
public void setUsers(Collection<User> users) {
this.users = users;
}
}
And here I have the user which the group relation as well the group is also 1 TO MANY because a user_admin can have multiple agents but agent could not have multiple admins.
#Entity
#Table(name = "user")
public class User {
private long id;
private String username;
private String password;
private boolean enabled = false;
private Role role;
private UserGroup userGroup;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User(String username, String password, Role role) {
this.username = username;
this.password = password;
this.role = role;
}
public User(String username, String password, boolean enabled, Role role, UserGroup userGroup) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.role = role;
this.userGroup = userGroup;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#NotBlank
#Column(nullable = false, updatable = false, unique = true)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#NotBlank
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(nullable = false, updatable = true)
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
#ManyToOne
#JoinColumn(name = "role_id", referencedColumnName = "id")
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
#ManyToOne
#JoinColumn(name = "user_group_id", referencedColumnName = "id")
public UserGroup getUserGroup() {
return userGroup;
}
public void setUserGroup(UserGroup userGroup) {
this.userGroup = userGroup;
}
}
At the time of creation of the user I specify the role and the group as well.
And in the SecurityConfig I have configure like this.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(Constants.BASE_URL_FILE_UPLOADER + Constants.URL_UPLOAD_FILE).hasRole("ADMIN") .anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.exceptionHandling().authenticationEntryPoint(new ContentSearcherAuthenticationEntryPoint());
}
But If I am accessing this end-point with the admin user I throws forbidden and also in the function when I access authentication.getAuthorities()it return emptyList
ResponseEntity<JsonNode> uploadFile(#RequestParam("file") MultipartFile file, Authentication authentication) {
logger.info("Authentication is [{}] and user is [{}]", authentication.getAuthorities(), authentication.getName()); // []
}
I am confused in UserDetailsService I have also added the GrantedAuthority as well like this.
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository userRepository;
private RoleRepository roleRepository;
public UserDetailsServiceImpl(UserRepository userRepository, RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username + " Not Exists");
}
user.setEnabled(true);
userRepository.save(user);
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.isEnabled(),
true, true, true, getAuthorities(user.getRole()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Role role) {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(Constants.ROLE_PREFIX + role.getName()));
return authorities;
}
}
What I am missing is there any more configuration I have to add ?
I am using JWT for the authentication purpose some thing should not to be added in this as well ?
Because when I get the Authentication in the JWT successfulAuthentication it shows the Authorities.
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
logger.info("Auth in the successful is [{}]", authResult.getAuthorities()); // [ROLE_ADMIN]
}
Using Spring-Security core 5.0.9.
I did it after getting the hint from #Reza Nasiri question basically I was not adding the Authorities when authenticating the JWT token now what I did is,
return new UsernamePasswordAuthenticationToken(user, null, getAuthorities("ADMIN");
In my getAuthentication function in JWTAuthorizationFilter class.
I am very new to developing. I am doing library management application in Java using spring boot. I am getting the above error while registering with the new user.
My application was running but I am getting the above error when I am doing anything like registering but, after clicking register button I am getting the above error.
Here is my code
My entity class:
#Entity
#Table(name="mytable")
public class User {
#Id
private int id;
private String username;
private String firstname;
private String lastname;
private int age;
private String password;
public User() {
}
public User(String username, String firstname, String lastname, int age, String password) {
super();
this.username = username;
this.firstname = firstname;
this.lastname = lastname;
this.age = age;
this.password = password;
}
omit getters and setters
Here is my controller:
#Controller
public class ApplicationController {
#Autowired
UserService userService;
#RequestMapping("/welcome")
public String Welcome(HttpServletRequest request) {
request.setAttribute("mode", "MODE_HOME");
return "welcomepage";
}
#GetMapping("/register")
public String registration(HttpServletRequest request) {
request.setAttribute("mode", "MODE_REGISTER");
return "welcomepage";
}
#PostMapping("/save-user")
public String registerUser(#ModelAttribute User user, BindingResult bindingResult, HttpServletRequest request) {
userService.saveMyUser(user);
request.setAttribute("mode", "MODE_HOME");
return "welcomepage";
}
#GetMapping("/show-users")
public String showAllUsers(HttpServletRequest request) {
request.setAttribute("users", userService.showAllUsers());
request.setAttribute("mode", "ALL_USERS");
return "welcomepage";
}
#RequestMapping("/delete-user")
public String deleteUser(#RequestParam int id, HttpServletRequest request) {
userService.deleteMyUser(id);
request.setAttribute("users", userService.showAllUsers());
request.setAttribute("mode", "ALL_USERS");
return "welcomepage";
}
#RequestMapping("/edit-user")
public String editUser(#RequestParam int id,HttpServletRequest request) {
request.setAttribute("user", userService.editUser(id));
request.setAttribute("mode", "MODE_UPDATE");
return "welcomepage";
}
#RequestMapping("/login")
public String login(HttpServletRequest request) {
request.setAttribute("mode", "MODE_LOGIN");
return "welcomepage";
}
#RequestMapping ("/login-user")
public String loginUser(#ModelAttribute User user, HttpServletRequest request) {
if(userService.findByUsernameAndPassword(user.getUsername(), user.getPassword())!=null) {
return "homepage";
}
else {
request.setAttribute("error", "Invalid Username or Password");
request.setAttribute("mode", "MODE_LOGIN");
return "welcomepage";
}
}
}
[here is my registration form when i run the application][1]
[1]: https://i.stack.imgur.com/33QvJ.png
Here is my application.properties
spring.mvc.view.prefix=/WEB-INF/view/
spring.mvc.view.suffix=.jsp
#Persistence Related Code
spring.datasource.url=jdbc:mysql://localhost/db_example?useSSL=false
spring.datasource.username=springuser
spring.datasource.password=ThePassword
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
logging.level.org.hibernate.SQL=debug
But, after clicking register button i am getting the above error. Can someone help me out?
There in Spring security 5 appear an interface as ReactiveUserDetailsService.
Now my question is how to implement UserDetailsService using:
Spring-data-Mongo
Spring Security (5)
That I have (users stored in memory)
#Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder().username("test").password("password").roles("USER").build();
UserDetails admin = User.withDefaultPasswordEncoder().username("admin").password("admin").roles("USER", "ADMIN").build();
return new MapReactiveUserDetailsService(user, admin);
}
What I want:
#Document(collection = "user")
public class User implements UserDetails {
#Id
private Long id;
private LocalDate createdAt;
private String username;
private String password;
private boolean accountNonLocker;
private boolean enabled;
#DBRef
private List<GrantedAuthority> grantedAuthorities;
//getters and setters
}
#Component
public class SecUserDetailsService implements ReactiveUserDetailsService {
}
And I don't know how to implement it. not found any resources on web.
Already found a solution:
Implement UserDetails interface in UseAccount class and create Role class that implements GrantedAuthority interface:
Role
#Data
#Document
public class Role implements GrantedAuthority {
#Id
private String id;
#Override
public String getAuthority() {
return id;
}
}
UseAcount
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Document
public class UserAccount implements UserDetails {
#Id
private String id;
private String username;
private String password;
private String firstName;
private String lastName;
private String idnp;
#Email
private String email;
#Builder.Default()
private boolean active = true;
#Builder.Default()
#DBRef
private List<Role> roles = new ArrayList<>();
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return active;
}
#Override
public boolean isAccountNonLocked() {
return active;
}
#Override
public boolean isCredentialsNonExpired() {
return active;
}
#Override
public boolean isEnabled() {
return active;
}
#Override
public String getName() {
return firstName + " " + lastName;
}
}
Define Security configuration and in it create web filter.
#Configuration
#EnableWebFluxSecurity
public class SecurityConfiguration {
#Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/login", "/logout").permitAll()
.pathMatchers("/i18n/**",
"/css/**",
"/fonts/**",
"/icons-reference/**",
"/img/**",
"/js/**",
"/vendor/**").permitAll()
.anyExchange()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutUrl("/logout")
.and()
.build();
}
//in case you want to encrypt password
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Create repository to extract your user from database.
And last step is to create UserDetailsService
#Component
public class SecUserDetailsService implements ReactiveUserDetailsService {
#Autowired
public ReactiveUserAccountRepository reactiveUserAccountRepository;
#Override
public Mono<UserDetails> findByUsername(String username) {
Mono<UserAccount> data = reactiveUserAccountRepository.findByUsername(username);
return data.cast(UserDetails.class);
}
}