Good Day.
I have a requirement to implement OAuth2 with spring-security using springboot2 basing on below URL.
URL Name:
https://www.devglan.com/spring-security/spring-boot-oauth2-jwt-example
And I did it.
Here I got another different requirement for the above code in authenticating user.
I need to pass companyId along with userName and password and grant_type(password)in x-www-form-urlencoded tab in postman tool.
And i have to fetch User based on username and companyId.
So please help me out what changes I need to do in above link code so that i can achieve my requirement.
Here i am getting only email. I need email along with companyId.
#Override
#Transactional
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email).orElseThrow(
() -> new UsernameNotFoundException("User Not Found with -> username or email : " + email));
return UserPrinciple.build(user);
}
Expected:
#Override
#Transactional
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmailAndCompanyId(email,companyId).orElseThrow(
() -> new UsernameNotFoundException("User Not Found with -> username or email : " + email));
return UserPrinciple.build(user);
}
At present with username and password it is working fine.
But in my system i have same user mapped to different companies.if same user mapped to multiple companies found i am getting error like below.
{
"error": "unauthorized",
"error_description": "query did not return a unique result: 2; nested exception is javax.persistence.NonUniqueResultException: query did not return a unique result: 2"
}
I need to fetch user, based on username and password and companyId which results in single user.
Can some one help on this issue. Thanks a lot in advance.
Bhanuprakash, i have a similar problem but temporary i did this:
In the "loadUserByUsername" method write this:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
System.out.println("compnayId: " + request.getParameter("companyId"));
I think there is a more elegant solution, but temporary i used this.
Thanks.
If you want additional information's with access tokens you can use TokenEnhancer class to do that.
CustomTokenEnhancer.java
public class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("id", user.getCompanyId());
additionalInfo.put("authorities", user.getAuthorities());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
Then user the instance of this class to void configure(AuthorizationServerEndpointsConfigurer endpoints) method like this
AuthorizationServerConfig.java
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.tokenEnhancer(new CustomTokenEnhancer());
}
This comment worked for me perfectly!
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
System.out.println("compnayId: " + request.getParameter("companyId"));
Related
I am working on a Spring Boot application that take the username and password of an existing user on the system and then generates a JWT token. I copied it from a tutorial and I changed it in order to work with my specific use cases. The logic is pretty clear to me but I have a big doubt about how the user is authenticated on the system. Following I will try to explain you as this is structured and what is my doubt.
The JWT generation token system is composed by two different micro services, that are:
The GET-USER-WS: this microservice simmply use Hibernate\JPA to retrieve the information of a specific user in the system. Basically it contains a controller class calling a service class that itself calla JPA repository in order to retrieve a specific user information:
#RestController
#RequestMapping("api/users")
#Log
public class UserController {
#Autowired
UserService userService;
#GetMapping(value = "/{email}", produces = "application/json")
public ResponseEntity<User> getUserByEmail(#PathVariable("email") String eMail) throws NotFoundException {
log.info(String.format("****** Get the user with eMail %s *******", eMail) );
User user = userService.getUserByEmail(eMail);
if (user == null)
{
String ErrMsg = String.format("The user with eMail %s was not found", eMail);
log.warning(ErrMsg);
throw new NotFoundException(ErrMsg);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
}
As you can see this controller contains an API that use the e-mail parameter (that is the username on the system) and return a JSON containing the information of this user.
Then I have a second microservice (named AUTH-SERVER-JWT) that is the one that call the previous API in order to obtain the user information that will be used to generate the JWT token. To keep the description as simple as possible it contains this controller class:
#RestController
//#CrossOrigin(origins = "http://localhost:4200")
public class JwtAuthenticationRestController {
#Value("${sicurezza.header}")
private String tokenHeader;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
#Qualifier("customUserDetailsService")
//private UserDetailsService userDetailsService;
private CustomUserDetailsService userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationRestController.class);
#PostMapping(value = "${sicurezza.uri}")
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtTokenRequest authenticationRequest)
throws AuthenticationException {
logger.info("Autenticazione e Generazione Token");
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
//final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final UserDetailsWrapper userDetailsWrapper = userDetailsService.loadCompleteUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetailsWrapper);
logger.warn(String.format("Token %s", token));
return ResponseEntity.ok(new JwtTokenResponse(token));
}
#RequestMapping(value = "${sicurezza.uri}", method = RequestMethod.GET)
public ResponseEntity<?> refreshAndGetAuthenticationToken(HttpServletRequest request)
throws Exception
{
String authToken = request.getHeader(tokenHeader);
if (authToken == null || authToken.length() < 7)
{
throw new Exception("Token assente o non valido!");
}
final String token = authToken.substring(7);
if (jwtTokenUtil.canTokenBeRefreshed(token))
{
String refreshedToken = jwtTokenUtil.refreshToken(token);
return ResponseEntity.ok(new JwtTokenResponse(refreshedToken));
}
else
{
return ResponseEntity.badRequest().body(null);
}
}
#ExceptionHandler({ AuthenticationException.class })
public ResponseEntity<String> handleAuthenticationException(AuthenticationException e)
{
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
}
private void authenticate(String username, String password)
{
Objects.requireNonNull(username);
Objects.requireNonNull(password);
try {
/// ???
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
}
catch (DisabledException e)
{
logger.warn("UTENTE DISABILITATO");
throw new AuthenticationException("UTENTE DISABILITATO", e);
}
catch (BadCredentialsException e)
{
logger.warn("CREDENZIALI NON VALIDE");
throw new AuthenticationException("CREDENZIALI NON VALIDE", e);
}
}
}
This class contains two method, the first one is used to generate a brand new JWT token and the second one it is used to refresh an existing JWT token. Consider now the first use case (generate a brand new JWT token) related to the createAuthenticationToken() method. This method take as input parmether the information related to the JWT token request: #RequestBody JwtTokenRequest authenticationRequest. Bascailly the JwtTokenRequest is a simple DTO object like this:
#Data
public class JwtTokenRequest implements Serializable
{
private static final long serialVersionUID = -3558537416135446309L;
private String username;
private String password;
}
So the payload in the body request will be something like this:
{
"username": "xxx#gmail.com",
"password": "password"
}
NOTE: in my DB I have a user having this username and password so the user will be retrieved and authenticated on the system.
As you can see the first effective operation that the createAuthenticationToken() method do is:
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
Basically it is calling the authenticate() method defined in the same class passing to it the previous credential ("username": "xxx#gmail.com" and "password": "password").
As you can see this is my authenticate() method
private void authenticate(String username, String password)
{
Objects.requireNonNull(username);
Objects.requireNonNull(password);
try {
/// ???
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
}
catch (DisabledException e)
{
logger.warn("UTENTE DISABILITATO");
throw new AuthenticationException("UTENTE DISABILITATO", e);
}
catch (BadCredentialsException e)
{
logger.warn("CREDENTIAL ERROR");
throw new AuthenticationException(""CREDENTIAL ERROR", e);
}
}
Basically it is passing these credential to the authenticate() method defined into the injected Spring Security AuthenticationManager instance, by this line:
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
This method seems to be able to authenticate or not these credentials. And it seems to works fine because if I put a wrong username or password it goes into the CREDENTIAL ERROR case and it throw the AuthenticationException exception.
And here my huge doubt: why it works?!?! How is it possible? If you come back to the createAuthenticationToken() controller method you can see that it does these two operation in the following order:
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetailsWrapper userDetailsWrapper = userDetailsService.loadCompleteUserByUsername(authenticationRequest.getUsername());
It first perform the authenticate() method (that should check if theusername and password that was sent are correct), then call the service method that retrieve the user information.
Sho how the authenticate() method is able to check if the credential sent in the original payload are correct?
Usually, the implementation of AuthenticationManager is a ProviderManager, which will loop through all the configured AuthenticationProviders and try to authenticate using the credentials provided.
One of the AuthenticationProviders, is DaoAuthenticationProvider, which supports a UsernamePasswordAuthenticationToken and uses the UserDetailsService (you have a customUserDetailsService) to retrieve the user and compare the password using the configured PasswordEncoder.
There is a more detailed explanation in the reference docs about the Authentication Architecture.
I am doing a project on library management system in spring boot security.
In order to calculate the fines for the issued books according to the roles i wan the current user role after borrowing a book.
Current user name, role book_id and fine will be stored in other table.
I am able to get the current users username, but not able to get role the current user.
Could someone please help me out!
//Part of Controller class
#RequestMapping("/homepage/borrowBook")
public String addBookings(Bookings bk, HttpServletRequest rqst) {
rqst.setAttribute("mode", "MODE_BORROW");
return "homepage";
}
#PostMapping("/homepage/save-borrow")
public String saveBorrow(Bookings bk, HttpServletRequest rqst, Authentication auth) {
rqst.setAttribute("mode", "MODE_BORROW");
if (BookRepo.exists(bk.getBook_id())) {
bk.setUser(auth.getName());
/////here i want the current user authority to be saved/checked.
bookingsRepo.save(bk);
return "homepage";
} else {
rqst.setAttribute("error", "Book doesn't exist");
return "homepage";
}
}
You can use Authentication.getAuthorities() to get the roles of the currently logged in user.
You can get the authorities using the SecurityContextHolder or through the inject Authentication object at your controller.
Find below through the SecurityContextHolder
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Collection<SimpleGrantedAuthority> list = (Collection<SimpleGrantedAuthority>) auth.getAuthorities();
for (SimpleGrantedAuthority permission : list) {
System.out.println(permission.getAuthority());
}
If you need any other information about the logged in user, you can access the UserDetails as follows
User userDetails = (User) auth.getPrincipal();
In this Spring Boot application there is a web service, which returns some data for a logged-in user:
#RequestMapping("/resource")
public Map<String, Object> home() {
Map<String, Object> model = new HashMap<String, Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
}
Imagine, the return value of the method depends on what user is currently logged in.
How can I find out, which user is logged in in that method?
As per request:
Spring Boot which uses Spring Security internally provides a SecurityContextHolder class which allows the lookup of the currently authenticated user via:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
The authentication instance now provides the following methods:
Get the username of the logged in user: getPrincipal()
Get the password of the authenticated user: getCredentials()
Get the assigned roles of the authenticated user: getAuthorities()
Get further details of the authenticated user: getDetails()
Since Spring Security 3.2 you can get currently logged in user (your implementation of UserDetails) by adding a parameter inside your controller method:
import org.springframework.security.web.bind.annotation.AuthenticationPrincipal;
#RequestMapping("/resource")
public Map<String, Object> home(#AuthenticationPrincipal User user) {
..
}
Replace User with the name of your class which implements UserDetails interface.
Edit:
Since Spring Security 4.0 annotation was moved to a different package:
import org.springframework.security.core.annotation.AuthenticationPrincipal;
Addendum:
This will work even in WebFlux reactive environment versus the SecurityContextHolder.getContext().getAuthentication() which won't work because of paradigm shift from thread per request model to multiple requests per thread.
You can simply use HttpServletRequest also to get user principle,
using HttpServletRequest request,
String user=request.getUserPrincipal().getName();
One way is to add java.security.Principal as a parameter as follows:
#RequestMapping("/resource")
public Map<String, Object> home(Principal principal) {
Map<String, Object> model = new HashMap<String, Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello " + principal.getName());
return model;
}
Since version 5.2 you can use CurrentSecurityContext annotation:
#GetMapping("/hello")
public String hello(#CurrentSecurityContext(expression="authentication?.name")
String username) {
return "Hello, " + username + "!";
}
In Spring boot v2.1.9.RELEASE if you are trying to get the name, email, given_name you can get those details from Pricipal.
Note: I am using spring security with google oauth2.
Map<String , Object> userDetails = ((DefaultOidcUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getAttributes();
System.out.println(userDetails.get("name"));
System.out.println(userDetails.get("email"));
System.out.println(userDetails.get("given_name"));
Recently using Keycloak authentication server and accessing currently logged-in user data is accessible like this
String userID;
KeycloakPrincipal kcPrincipal = getPrincipal();
KeycloakSecurityContext ksContext = kcPrincipal.getKeycloakSecurityContext();
IDToken idToken = ksContext.getToken();
userID = idToken.getName();
Im using spring boot 2.0 with OAuth so I'm doing it like this
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Object pricipal = auth.getPrincipal();
String user="";
if (pricipal instanceof DefaultOidcUser) {
user = ((DefaultOidcUser) pricipal).getName();
}
You can find the currently logged in user name without using any spring Security features.
All you need is a jdk 1.8
Do the following :
#RequestMapping("/login")
#Override
public ModelAndView AuthChecker(#RequestParam("email") String email, #RequestParam("password") String password, Customers cust) {
ModelAndView mv = new ModelAndView("index");
if((repo.findByEmail(email)!=null) && (repo.findByPassword(password)!=null)) {
List<Customers> l= repo.findAll();
cust = (Customers) l.stream()
.filter(x -> email.equals(x.getEmail()))
.findAny()
.orElse(null);
mv.addObject("user",cust.getName());
mv.setViewName("DashBoardRedirect");
return mv;
Once name fetched successfully, you can use the same in any jsp/thymeleaf view.
I'm using this ContainerRequestFilter to check HTTP Basic credentials.
private class Filter implements ResourceFilter, ContainerRequestFilter {
#Override
public ContainerRequest filter(ContainerRequest request) {
String auth = request.getHeaderValue("Authorization");
if (auth == null || !auth.startsWith("Basic ")) {
throw new NotAuthorizedException("FAILED\n");
}
auth = Base64.base64Decode(auth.substring("Basic ".length()));
String[] vals = auth.split(":");
String username = vals[0];
String password = vals[1];
boolean validUser = database.Users.validate(username, password);
if (!validUser) {
throw new NotAuthorizedException("FAILED\n");
}
return request;
}
...
}
So by the time I get to this point, I've authenticated the user. Now how I can get the username?
#GET
#Path("some_kind_of_report_or_something")
#Produces(MediaType.TEXT_PLAIN)
public String fetchAReportOrSomething() {
// At this point, I know that the user has provided good credentials,
// now I need get the user's username as a String
String username = ???;
}
I suppose I could use HttpContext.getRequest() and do the same thing as in the AuthFilter (I'd move that username/password extraction logic to its own method). In the filter, can I somehow store the extracted username somewhere in the request object so it gets passed on to this handler?
(By the way, is there a better way to extract the username and password than what I've done in the filter? If so, let me know in a comment.)
This blog entry should enlighten you:
http://plaincode.blogspot.pt/2011/07/openid-authentication-example-in-jersey.html
Take a look how it's done in a working application: www.s3auth.com. The source code is available at github. As you can see on the site, facebook and google authentication mechanisms are used. The application is using JAX-RS/Jersey.
I'm using Spring Security 3 and Spring MVC 3.05.
I would like to print username of currently logged in user,how can I fetch UserDetails in my Controller?
#RequestMapping(value="/index.html", method=RequestMethod.GET)
public ModelAndView indexView(){
UserDetails user = ?
mv.addObject("username", user.getUsername());
ModelAndView mv = new ModelAndView("index");
return mv;
}
If you already know for sure that the user is logged in (in your example if /index.html is protected):
UserDetails userDetails =
(UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
To first check if the user is logged in, check that the current Authentication is not a AnonymousAuthenticationToken.
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
// userDetails = auth.getPrincipal()
}
Let Spring 3 injection take care of this.
Thanks to tsunade21 the easiest way is:
#RequestMapping(method = RequestMethod.GET)
public ModelAndView anyMethodNameGoesHere(Principal principal) {
final String loggedInUserName = principal.getName();
}
If you just want to print user name on the pages, maybe you'll like this solution. It's free from object castings and works without Spring Security too:
#RequestMapping(value = "/index.html", method = RequestMethod.GET)
public ModelAndView indexView(HttpServletRequest request) {
ModelAndView mv = new ModelAndView("index");
String userName = "not logged in"; // Any default user name
Principal principal = request.getUserPrincipal();
if (principal != null) {
userName = principal.getName();
}
mv.addObject("username", userName);
// By adding a little code (same way) you can check if user has any
// roles you need, for example:
boolean fAdmin = request.isUserInRole("ROLE_ADMIN");
mv.addObject("isAdmin", fAdmin);
return mv;
}
Note "HttpServletRequest request" parameter added.
Works fine because Spring injects it's own objects (wrappers) for HttpServletRequest, Principal etc., so you can use standard java methods to retrieve user information.
That's another solution (Spring Security 3):
public String getLoggedUser() throws Exception {
String name = SecurityContextHolder.getContext().getAuthentication().getName();
return (!name.equals("anonymousUser")) ? name : null;
}
if you are using spring security then you can get the current logged in user by
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName(); //get logged in username
You can use below code to find out principal (user email who logged in)
org.opensaml.saml2.core.impl.NameIDImpl principal =
(NameIDImpl) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String email = principal.getValue();
This code is written on top of SAML.