Good day developers. I'm trying to retrieve some data for a authenticated user in my application in order to implement it in other method.This app uses Spring Security and JWT, thus the first thing to do was setting the implemetation of Java interface UserDetails on the class UserDetailsImpl as following:
package com.example.demo.services;
import com.example.demo.entiities.Renter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.*;
import java.util.stream.Collectors;
public class UserDetailsImpl implements UserDetails {
public static final long serialVersionUID=1L;
private Long id;
private String username;
private String email;
@JsonIgnore
private String password;
private Collection<? extends GrantedAuthority> authorities;
public UserDetailsImpl(Long id, String username, String email, String password,
Collection<? extends GrantedAuthority> authorities) {
this.id = id;
this.username = username;
this.email = email;
this.password = password;
this.authorities = authorities;
}
public static UserDetailsImpl build(Renter renter) {
List<GrantedAuthority> authorities = renter.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(renter.getRenterName()))
.collect(Collectors.toList());
return new UserDetailsImpl(
renter.getId(),
renter.getRenterName(),
renter.getRenbterEmail(),
renter.getRenterPassword(),
authorities);
}//this method would return the new user logged details accessed through the entity Renter and each
//method i need neccesary for my app comsumption , like getting the name , email, password,etc...
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public Long getId() {
return id;
}
public String getEmail() {
return email;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
UserDetailsImpl user = (UserDetailsImpl) o;
return Objects.equals(id, user.id);
}
}
With that implementation of the Userdetails interface , is time to also implement the UserDeailsService interface too in order to get them the UserDetails object, thus its implementation would be :
package com.example.demo.services;
import com.example.demo.entiities.Renter;
import com.example.demo.jwt.JwtUtils;
import com.example.demo.repositories.RenterRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
RenterRepository renterRepository;
@Override
@Transactional
public UserDetails loadUserByUsername(String renterName) throws UsernameNotFoundException {
Renter renter = renterRepository.findByRenterName(renterName)
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + renterName));
return UserDetailsImpl.build(renter);
}
}
thus i get full custom Renter object using RenterRepository, then i build a UserDetails object using static build() method.
Thus next step would be setting all logic to filter the token created in every user login request, and from there with that token generated and triggering the SecurityContextHolder i might be able to access the user authenticated details, thus:
package com.example.demo.jwt;
import com.example.demo.services.UserDetailsServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class AuthTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsServiceImpl userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String username = jwtUtils.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username)
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Cannot set user authentication: {}", e);
}
filterChain.doFilter(request, response);
}
Thus in this case basically inside the try statement under the condition of the generated token being valid , first i extract the user name from the token . Then from that username i got the UserDetails to create an Authentication object accessing the method of its service(loadUserByname); and after that
got settled the current UserDetails in SecurityContext using setAuthentication(authentication) method, that would be used in further implemenbtations to access the user data(didn't work)
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7, headerAuth.length());
}
return null;
}
}
Then login payload is created achieving from user interaction all data neccesary for the authetication process on a class LoginRequest. An the login implementation with the service and its implementation would be set in this way
RENTER SERVICE
package com.example.demo.services;
import com.example.demo.entiities.Renter;
import com.example.demo.exceptions.GeneralException;
import com.example.demo.payload.LoginRequest;
import org.springframework.http.ResponseEntity;
import java.util.List;
import java.util.Map;
public interface RenterService {
ResponseEntity<?> loginUser(LoginRequest loginRequest)throws GeneralException;
}
RENTER SERVICE IMPLEMENTATION
package com.example.demo.services;
import com.example.demo.dto.RenterDtos;
import com.example.demo.entiities.EnumRoles;
import com.example.demo.entiities.Renter;
import com.example.demo.entiities.Role;
import com.example.demo.exceptions.GeneralException;
import com.example.demo.exceptions.NotFoundException;
import com.example.demo.jsons.RenterJson;
import com.example.demo.jwt.JwtUtils;
import com.example.demo.payload.LoginRequest;
import com.example.demo.payload.SignUprequest;
import com.example.demo.repositories.RenterRepository;
import com.example.demo.repositories.RoleRepository;
import com.example.demo.responses.JwtResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.HttpServerErrorException;
import java.util.*;
import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import javax.validation.Valid;
@Service
public class RenterServiceImpl implements RenterService {
@Autowired
private RenterRepository renterRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
AuthenticationManager authenticationManager;
@Autowired
JwtUtils jwtUtils;
private static final Logger LOGGER = LoggerFactory.getLogger(RenterServiceImpl.class);
@Override
public ResponseEntity<?> loginUser(LoginRequest loginRequest) throws GeneralException {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getRenterName(), loginRequest.getRenterPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream()
.map(item -> item.getAuthority())
.collect(Collectors.toList());
return ResponseEntity.ok(new JwtResponse(jwt,
userDetails.getId(),
userDetails.getUsername(),
userDetails.getEmail(),
roles));
}
private static List<GrantedAuthority> mapRoleUser(List<String> roles){
List<GrantedAuthority>authorities=new ArrayList<>();
for (String role : roles){
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
Then the controller with its endpoint calling all this :
package com.example.demo.controller;
import com.example.demo.dto.RenterRegisterDto;
import com.example.demo.entiities.EnumRoles;
import com.example.demo.jsons.CreateRenterJson;
import com.example.demo.jsons.RenterJson;
import com.example.demo.jwt.JwtUtils;
import com.example.demo.payload.LoginRequest;
import com.example.demo.payload.SignUprequest;
import com.example.demo.repositories.RenterRepository;
import com.example.demo.repositories.RoleRepository;
import com.example.demo.responses.AppResponse;
import com.example.demo.entiities.Renter;
import com.example.demo.entiities.Role;
import com.example.demo.exceptions.GeneralException;
import com.example.demo.responses.JwtResponse;
import com.example.demo.services.RenterService;
import com.example.demo.services.UserDetailsImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/cubancoder/multirenter")
public class RegistrationController {
@Autowired
RenterService renterService;
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RenterRepository renterRepository;
@Autowired
RoleRepository roleRepository;
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
JwtUtils jwtUtils;
@ResponseStatus(HttpStatus.OK)
@PostMapping(value = "/login/renter")
public AppResponse<ResponseEntity<?>> logInUser(@Valid @RequestBody LoginRequest loginRequest) throws GeneralException {
return new AppResponse<>("Success",String.valueOf(HttpStatus.CREATED),
"Ok",renterService.loginUser(loginRequest));
}
}
Until this point the user logged is achieved and all data referring to it is ok. On a debug process i check what the implementation brings and is fine .
THE PROBLEM STARTS HERE
Then having in mind that user logged i would like to access its data from other service implementation in order to boost other app features, thus lets say i have a service and its implementation called ProductServiceImpl, and in this classes i initialize methods that brings me all products, but i also want to know which user is doing that request,thus if it is logged i need all the data , otherwise the app would do other stuff. Having in mind once the user is autheticated with token a SecurityContextHolder is already created setting the user details for that request, i guess all would be such simple as calling that SecurityContextHolder wherever i need to access the data the user logged brings right?
package com.example.demo.services;
import com.example.demo.dto.ProductDtos;
import com.example.demo.dto.RenterDtos;
import com.example.demo.entiities.*;
import com.example.demo.exceptions.GeneralException;
import com.example.demo.exceptions.NotFoundException;
import com.example.demo.jwt.AuthEntryPointJwt;
import com.example.demo.jwt.JwtUtils;
import com.example.demo.payload.LoginRequest;
import com.example.demo.repositories.ProductRepository;
import com.example.demo.repositories.RenterRepository;
import com.example.demo.security.AuthenticationValidation;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
ProductRepository productRepository;
@Autowired
AuthenticationManager authenticationManager;
@Autowired
UserDetailsService userDetailsService;
@Autowired
ProductDtos productDtos;
@Autowired
AuthEntryPointJwt authEntryPointJwt;
@Autowired
RenterDtos renterDtos;
@Autowired
RenterRepository renterRepository;
@Autowired
JwtUtils jwtUtils;
@Autowired
LoginRequest loginRequest;
public Map<String, Object> getAllProducts() throws GeneralException {
Map<String, Object> dto = new HashMap<>();
List<Product> listProducts = productRepository.findAll();
Option1:
Object auth = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
.....doing something here .........
Option2:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth.isAuthenticated()) {
Object Authenticated = auth.getPrincipal();
String renterLogged = ((UserDetails) Authenticated).getUsername();
.....doing something........
}
return dto;
}
}
BUT THE CONTEXT HOLDER BRINGS ME AN ANONYMOUS USER!!! not letting me proceed cause of a error saying that :
ava.lang.ClassCastException: java.lang.String cannot be cast to org.springframework.security.core.userdetails.UserDetails
at com.example.demo.services.ProductServiceImpl.getAllProducts(ProductServiceImpl.java:83) ~[classes/:na]...
And honestly i quite strayed about what else to do!. In my debug process in order to check :
Last but not the least in my security package its class is set in this way :
package com.example.demo.security;
import com.example.demo.entiities.Renter;
import com.example.demo.exceptions.NotFoundException;
import com.example.demo.jwt.AuthEntryPointJwt;
import com.example.demo.jwt.AuthTokenFilter;
import com.example.demo.repositories.RenterRepository;
import com.example.demo.services.ProductServiceImpl;
import com.example.demo.services.RenterService;
import com.example.demo.services.UserDetailsImpl;
import com.example.demo.services.UserDetailsServiceImpl;
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.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;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
// securedEnabled = true,
// jsr250Enabled = true,
prePostEnabled = true)public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
RenterService renterService;
@Autowired
UserDetailsServiceImpl userDetailsService;
@Autowired
ProductServiceImpl productService;
@Autowired
private AuthEntryPointJwt unauthorizedHandler;
@Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/cubancoder/multirenter/**","/v2/api-docs","/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**").permitAll()
.antMatchers("/api/test/**").permitAll()
.anyRequest().authenticated();
http.logout().logoutUrl("/cubancoder/multirenter/logout");
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
Any help would be amazing .Please!!
My solution was:
UserDetailsImpl currentUser = (UserDetailsImpl) authentication.getPrincipal();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.