I have a Spring Security + Spring Session + Spring Boot project and a Controller with a custom login POST method. I have my own means to verify the credentials of the customer. Once the verification is successful, what's the best practice to access the session and set the identification to true?
I've tried this with no results:
#PostMapping("/login")
public ResponseEntity loginSubmit(#RequestBody LoginForm form) {
Errors errors = authenticationService.validateLoginForm(form);
if (!errors.hasErrors()) {
CustomerDTO result = authenticationService.findCustomerByEmailAndPassword(form);
boolean success = (result != null && result.getId() != null);
SecurityContextHolder.getContext().getAuthentication().setAuthenticated(success);
return new ResponseEntity(result, HttpStatus.OK);
} else {
return new ResponseEntity(errors.getAllErrors(), HttpStatus.OK);
}
}
How should I be doing this instead?
Instead of writing your own endpoint I would suggest to integrate in Spring Security framework by implementing your own org.springframework.security.authentication.AuthenticationProvider
Or simply do this
public void login(HttpServletRequest req, String user, String pass) {
UsernamePasswordAuthenticationToken authReqz = new UsernamePasswordAuthenticationToken(user, pass);
Authentication auth = authManager.authenticate(authReq);
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(auth);
HttpSession session = req.getSession(true);
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, sc);
}
Detailed explanation can be found here
Related
I have an application with #CustomFormAuthenticationMechanismDefinition, and I would like to log the username, session id, IP address, etc. both at login and at logout. The HttpAuthMechanism that gets applied with this annotation associate the given session with the principal, which I can access through the SecurityContext. With a direct logout, I have no problem logging, but I would also like to log when session times out. So I created a HttpSessionListener and in its sessionDestroyed() method I try to access the logged in user through SecurityContext, but it returns an empty set, maybe because the securityContext got invalidated already.
One solution I have in my mind is to store the user principal in a session parameter (which likely happens with the HttpAuthMechanism implementation) and access it from there from the HttpSessionEvent object, but that doesn't feel like the cleanest solution. Is there another Listener I can use or some other solution?
I went with the custom HttpAuthenticationMechanism, here is it if anyone would need it (though I would be more than glad to have some feedback on whether or not it has any security flaws, or improvements).
In an #ApplicationScoped class implementing HttpAuthenticationMechanism:
#Override
public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthenticationException {
if (!httpMessageContext.isProtected()) {
return httpMessageContext.doNothing();
}
HttpSession session = request.getSession(false);
Credential credential = httpMessageContext.getAuthParameters().getCredential();
// If we already have a session, we get the user from it, unless it's a new login
if (session != null && !(credential instanceof UsernamePasswordCredential)) {
User user = (User) session.getAttribute("user");
if (user != null) {
return httpMessageContext.notifyContainerAboutLogin(user, user.getRoles());
}
}
// If we either don't have a session or it has no user attribute, we redirect/forward to login page
if (!(credential instanceof UsernamePasswordCredential)) {
return redirect(request, response, httpMessageContext);
}
// Here we have a Credential, so we validate it with the registered IdentityStoreHandler (injected as idStoreHandler)
CredentialValidationResult validate = idStoreHandler.validate(credential);
Context context = new Context();
context.setIp(request.getRemoteAddr());
if (validate.getStatus() == CredentialValidationResult.Status.VALID) {
session = request.getSession(true);
CallerPrincipal callerPrincipal = validate.getCallerPrincipal();
session.setAttribute("user", callerPrincipal);
context.setUser(callerPrincipal);
context.setSessionId(session.getId());
Logger log = new Logger(logger, "validateRequest", context);
log.debug("Logged in user: " + callerPrincipal.getName());
String redirectPage = "whatYouWant.xhtml";
redirect(request, response, httpMessageContext, redirectPage);
return httpMessageContext.notifyContainerAboutLogin(validate);
} else if (validate.getStatus() == CredentialValidationResult.Status.NOT_VALIDATED) {
return redirect(request, response, httpMessageContext);
} else {
// Logging
return httpMessageContext.responseUnauthorized();
}
}
And in an implemented HttpSessionListener:
#Override
public void sessionDestroyed(HttpSessionEvent se) {
User user = (User) se.getSession().getAttribute("user");
if (user != null) {
// logging
}
}
I got this task as an intern in a company to implement refresh token on their system. I can see that there are some codes that are already made for it (someone already tried to implement it)but it's not working.
I traced the code and found that it creates and prints the refresh token on the console but not on the ResponseEntity (returns null). I'm not used to coding in Java EE, Spring Framework, JWT Tokens or OAuth. I still have a lot of things to learn. I saw some answers that it needs to have an access type that is offline but the codes are all in PHP. I don't know how to implement it on Java with Spring Framework. So here are some of the codes...
For the Login Controller:
public ResponseEntity<String> auth(#RequestBody AuthModel auth) {
String username = auth.getUsername();
String password = auth.getPassword();
UserModel user = authService.authenticate(username, password);
if (user != null) {
final Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
auth.getUsername(),
auth.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication); //set current user
final UserDetails userDetails = authService.loadUserByUsername(auth.getUsername());
user.setOrg_id(usrService.setOrgId(user.getCompany_id()));
return new ResponseEntity(new ResponseModel(HttpStatus.OK.value(),jwtService.getToken(userDetails),jwtService.getRefreshToken(userDetails), "Success"), HttpStatus.OK);
}
For the Services:
#SuppressWarnings("static-access")
public String getRefreshToken(UserDetails user)
{
System.out.println("Get Refresh Token: " + createRefreshToken(user));
return createRefreshToken(user);
}
public String createRefreshToken(UserDetails userDetails) {
if (userDetails.getUsername() == null) {
throw new IllegalArgumentException("Cannot create JWT Token without username");
}
Claims claims = Jwts.claims().setSubject(userDetails.getUsername());
claims.put("roles", Arrays.asList(userDetails.getAuthorities()));
Date now = new Date();
String token = Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setId(UUID.randomUUID().toString())
.setIssuedAt(now)
.setExpiration(getRefreshExpirationTime())
.signWith(SignatureAlgorithm.HS512, refreshEncodedSecret)
.compact();
//System.out.println("Refresh Token: " + token);
return token;
}
After a success login, it returns this Response.
ResponseEntity
And for the console...
Console result
I hope you can answer me in details. Thank you in advance!
I am working on a Java desktop application and after some search I was able to authenticate the user using RestTemplate. Now the situation is I have the cookie String at the desktop side(code given below). Now what I would like to do is to do two important things, get which user logged in using that cookie and access(GET,POST,DELETE) secured resources which are marked with #Secured or #PreAuthorize annotation.
here is my authentication code :
#Override
public void initialize(URL location, ResourceBundle resources) {
submitButton.setOnAction(event -> {
if(!(usernameField.getText().isEmpty() && passwordField.getText().isEmpty())){
try {
RestTemplate rest = new RestTemplate();
String jsessionid = rest.execute("http://localhost:8080/j_spring_security_check", HttpMethod.POST,
new RequestCallback() {
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
request.getBody().write(("j_username=" + usernameField.getText() + "&j_password=" + passwordField.getText()).getBytes());
}
}, new ResponseExtractor<String>() {
#Override
public String extractData(ClientHttpResponse response) throws IOException {
List<String> cookies = response.getHeaders().get("Cookie");
// assuming only one cookie with jsessionid as the only value
if (cookies == null) {
cookies = response.getHeaders().get("Set-Cookie");
}
String cookie = cookies.get(cookies.size() - 1);
System.out.println("Cookie is "+cookie);
int start = cookie.indexOf('=');
int end = cookie.indexOf(';');
return cookie.substring(start + 1, end);
}
});
// rest.put("http://localhost:8080/rest/program.json;jsessionid=" + jsessionid, new DAO("REST Test").asJSON());
} catch (AuthenticationException e) {
System.out.println("AuthenticationException");
}
} else {
System.out.println("Fields are empty");
}
});
}
Output of program is :
DEBUG: org.springframework.web.client.RestTemplate - Created POST request for "http://localhost:8080/j_spring_security_check"
DEBUG: org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/j_spring_security_check" resulted in 302 (Found)
Cookie is JSESSIONID=903B2924CCC84421931D52A4F0AA3C7E; Path=/; HttpOnly
If I was on server-side, I would have simply called the below method to get the currently authenticated user :
#Override
public Person getCurrentlyAuthenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
} else {
return personDAO.findPersonByUsername(authentication.getName());
}
}
How can I get the currently authenticate user on desktop based java app so I can just use below method and authenticate on desktop java app. :
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication authentication = new UsernamePasswordAuthenticationToken(person1, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
This way, I can use #Secured annotations for the desktop java app as well. Thanks a lot.
Update
So on the server side I have created a method which gives me the logged in user. As suggested in an answer, I can use the same rest template, but I would like to store the cookie in users local db instead of passing the Resttemplates object around when user clicks here and there.
Server side method :
#Secured("ROLE_USER")
#RequestMapping(value = "/rest/getloggedinuser", method = RequestMethod.GET)
public
#ResponseBody
ResponseEntity<RestPerson> getLoggedInRestUser() {
Person person = this.personService.getCurrentlyAuthenticatedUser();
RestPerson restPerson = new RestPerson();
restPerson.setFirstname(person.getFirstName());
restPerson.setUsername(person.getUsername());
restPerson.setPassword("PROTECTED");
return new ResponseEntity<RestPerson>(restPerson, HttpStatus.OK);
}
Now, next for now, I am trying to use the same RestTemplate to check if this method works with code below, but I would really like to know how I can do this with just a cookie :
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", cookie);
HttpEntity requestEntity = new HttpEntity(null, requestHeaders);
ResponseEntity rssResponse = rest.exchange(
"/rest/getloggedinuser",
HttpMethod.GET,
requestEntity,
Person.class);
String rssResponseBody = (String)rssResponse.getBody();
System.out.println("Response body is ");
Is there a way to cast the Object in the ResponseBody to the Person object???
If you want to get some user information which is stored on the server-side, you should create a new service, for example "getUserInformation" on your server, which will provide such information.
You should not extract cookies manually, just reuse the same RestTemplate, it stores cookies internally (specifically in the underlying HttpClient). That's how you can reach secure resources.
UPDATE:
You don't need to pass around the RestTemplate, just make it a singleton and use it everywhere.
And rssResponse.getBody(); should return you a Person object, not String.
I am creating a Spring MVC web application using spring security for authentication.
My application has a requirement that the user is logged in automatically when the user accesses a particular URL, say http://www.example.com/login?username=xyz
The user xyz is in the database, and I need to create a session for this user automatically.
Request you to let me know how this can be achieved.
Thanks!
You can do something like this, the idea would be to retrieve the user from the database, make the checks you want and then use the UsernamePasswordAuthenticationToken to create a new Principal into the current session.
The use of the returned Principal of the method is for subsequent uses on the same request, as the Principal won't be available to be retrieved by normal means until the next request.
public Principal doAutoLogin(String username, String password, HttpServletRequest hreq, HttpServletResponse hres, boolean rememberMe) {
User user = getUserFromDatabase(username);
if(user != null && passwordsMatch(password, user.getPassword())) {
try {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
token.setDetails(new WebAuthenticationDetails(hreq));
if(LOG.isDebugEnabled())
LOG.debug("Logging in with "+ token.getPrincipal());
SecurityContextHolder.getContext().setAuthentication(token);
if(rememberMe) {
rememberMeServices.onLoginSuccess(hreq, hres, token);
}
return token;
} catch (Exception e) {
SecurityContextHolder.getContext().setAuthentication(null);
LOG.error("Failure in autoLogin", e);
return null;
}
}
else {
throw new IllegalArgumentException("The specified credentials can't be found");
}
}
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.