UserDetails getPassword returns null in spring security 3.1. How to get password of currently logged in user? - spring

I have implemented change password functionality using spring security but ((UserDetails) principal).getPassword()) is returning null for logged in user.
If I remember correctly, this used to work earlier in 3.0. Was this changed in 3.1 so that the current password of the logged in user cannot be retrieved?
In the below code I am checking the current password typed in user password from the web page. I am then checking if the logged in user's password matches the typed in password. If it does then I want to set oldPasswordMatchNewPassword = true.
How do I implement this functionality?
#RequestMapping(value = "/account/changePassword.do", method = RequestMethod.POST)
public String submitChangePasswordPage(
#RequestParam("oldpassword") String oldPassword,
#RequestParam("password") String newPassword) {
Object principal = SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
String username = principal.toString();
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
System.out.println("username: " + username);
System.out.println("password: "
+ ((UserDetails) principal).getPassword());
if (((UserDetails) principal).getPassword() != null) {
if (((UserDetails) principal).getPassword().equals(oldPassword)) {
oldPasswordMatchNewPassword = true;
}
}
}
if (oldPasswordMatchNewPassword == true) {
logger.info("Old password matches new password. Password will be updated.");
changePasswordDao.changePassword(username, newPassword);
SecurityContextHolder.clearContext();
return "redirect:home.do";
} else {
logger.info("Old password did not match new password. Password will be not be updated.");
return null;
}
}
I put a couple of sysout()s so that I can see the values returned.
For ((UserDetails) principal).getUsername() I can see the correct logged in user.
((UserDetails) principal).getPassword() it is returning null.
How do I get ((UserDetails) principal).getPassword() this value?
Thanks in advance!

I used this block of code (erase-credentials="false") to fix this. I do not know if this is an elegant solution but it fixed my problem:
<authentication-manager alias="authenticationManager" erase-credentials="false">
<!-- authentication-provider user-service-ref="userService" -->
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource" />
</authentication-provider>
</authentication-manager>

Yes, this has changed in version 3.1. Credentials are cleared after a successfull authentication by default. You can set eraseCredentialsAfterAuthentication to false on the ProviderManager to prevent this.
See details here: http://static.springsource.org/spring-security/site/docs/3.2.x/reference/core-services.html#core-services-erasing-credentials

Since the password isn't retained in memory after the user has been authenticated (generally a good thing), you would need to explicitly reload it in order to use it. An alternative and more flexible strategy is to inject an instance of the AuthenticationManager and use that directly:
String name = SecurityContextHolder.getContext().getAuthentication();
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(name, oldPassword));
// Update password here with your dao
} catch (AuthenticationException e) {
// Old password was wrong
}
that way you don't need to worry about things like password-encoding strategies. Note that you shouldn't be storing passwords in plain text. They should be hashed using bcrypt or something similar.

Related

How to set the Session using Spring boot

i am creating simple login programming using seesion.iam a beginner of spring boot how to set the seesion if the username and password correct below it is work successfully if the username and password correct vist to the home page.how to set the session . i want show the seesion on index page.logout also needed
Login
#PostMapping("/login")
public String login(#ModelAttribute("user") User user ) {
if(Objects.nonNull(oauthUser))
{
((User) httpSession).getUsername();
return "redirect:/";
} else {
return "redirect:/login";
}
}
index.html
<h2>Welcome<h2>
You can inject session info directy with
#Autowired
private HttpSession httpSession;
and to get id httpSession.getId()

Get current logged in user from Spring when SessionCreationPolicy.STATELESS is used

I want to implement this example using Keyclock server with Spring Security 5.
I'm going to use OAuth2.0 authentication with JWT token. I'm interested how I can get the current logged in user into the Rest Endpoint?
I have configured Spring Security not to store user sessions using http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);.
One possible way is to use this code:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
But I don't know is it going to work. Can someone give some advice for that case?
SecurityContextHolder, SecurityContext and Authentication Objects
By default, the SecurityContextHolder uses a ThreadLocal to store these details, which means that the security context is always available to methods in the same thread of execution. Using a ThreadLocal in this way is quite safe if care is taken to clear the thread after the present principal’s request is processed. Of course, Spring Security takes care of this for you automatically so there is no need to worry about it.
SessionManagementConfigurer consist of isStateless() method which return true for stateless policy. Based on that http set the shared object with NullSecurityContextRepository and for request cache NullRequestCache. Hence no value will be available within HttpSessionSecurityContextRepository. So there might not be issue with invalid/wrong details for user with static method
Code:
if (stateless) {
http.setSharedObject(SecurityContextRepository.class,
new NullSecurityContextRepository());
}
if (stateless) {
http.setSharedObject(RequestCache.class, new NullRequestCache());
}
Code:
Method to get user details
public static Optional<String> getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(extractPrincipal(securityContext.getAuthentication()));
}
private static String extractPrincipal(Authentication authentication) {
if (authentication == null) {
return null;
} else if (authentication.getPrincipal() instanceof UserDetails) {
UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
return springSecurityUser.getUsername();
} else if (authentication.getPrincipal() instanceof String) {
return (String) authentication.getPrincipal();
}
return null;
}
public static Optional<Authentication> getAuthenticatedCurrentUser() {
log.debug("Request to get authentication for current user");
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication());
}
sessionManagement
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
You might like to explore Methods with Spring Security to get current user details with SessionCreationPolicy.STATELESS
After the service validate the token, you can parse it, and put it into the securitycontext, it can contains various data, so you have to look after it what you need. For example, subject contains username etc...
SecurityContextHolder.getContext().setAuthentication(userAuthenticationObject);
The SecurityContextHolder's context maintain a ThreadLocal entry, so you can access it on the same thread as you write it in the question.
Note that if you use reactive (webflux) methodology, then you have to put it into the reactive context instead.

Password Policy errors not being thrown with LDAP Spring Security

I have am fairly new to Spring Security with LDAP and I am trying to authenticate with a user who has a password expired on the LDAP server(FreeIPA).
I cannot seem to trigger any password expired exception etc. The Password Policy Response Controls always return null....
Here is my code, perhaps I am doing something incorrectly. How do I handle password policy errors? They don't fire at all currently.
<bean id="freeIpaContextSource" class="org.springframework.security.ldap.ppolicy.PasswordPolicyAwareContextSource">
<constructor-arg value="${logon.freeipa.zone.ldap.connection.url}"/>
<property name="base" value="${logon.freeipa.zone.user.dn.base}"/>
</bean>
<bean id="freeIpaLdapTemplate" class="org.springframework.security.ldap.SpringSecurityLdapTemplate">
<constructor-arg name="contextSource" ref="freeIpaContextSource"/>
</bean>
I have a custom LdapAuthenticator below which uses a ldaptemplate to authenticate users.
#Override
public DirContextOperations authenticate(Authentication authentication) {
checkForIllegalStateDuringAuthentication(authentication);
logger.info(String.format("*** Beginning to authenticate against LDAP zone %s ***", authorizationZone.getName()));
zoneAuthenticationService.saveRequestDataInSession((UsernamePasswordAuthenticationToken) authentication, authorizationZone.getName());
CollectingAuthenticationErrorCallback errorCallback = new CollectingAuthenticationErrorCallback();
boolean isAuthenticated = false;
String userName = authentication.getName();
String password = authentication.getCredentials().toString();
String filterLookup = buildLDAPFilterLookupString(userName);
if (StringUtils.isNotBlank(password)) {
logger.info(String.format("*** Attempting authentication for user %s ***", userName));
try {
isAuthenticated = ldapTemplate.authenticate(StringUtils.EMPTY, filterLookup, password, errorCallback);
} catch (Exception exception) {
errorCallback.execute(exception);
}
}
if (!isAuthenticated) {
if (errorCallback.getError() == null) {
errorCallback.execute(new AuthenticationException(null));
}
//Any LDAP exception caught are stored inside the errorCallBack for use later to display an appropriate error.
logger.error(String.format("*** Authentication for user %s has failed. Exception has occurred while system performed LDAP authentication. ***", userName), errorCallback.getError());
throw new LdapAuthenticationException(errorCallback.getError().getMessage(), errorCallback.getError());
}
logger.info(String.format("*** Authentication for user %s has succeeded ***", userName));
return new DirContextAdapter(buildAuthenticatedDN(userName));
}
No matter what I do I cannot get any password policy errors to return. From my understanding you need to set a request control with PasswordPolicyControl when attempting to authenticate, but I never receive any response controls from the server. I have tried implementing something like below, but no luck on anything.
LdapContext context = (LdapContext)ldapTemplate.getContextSource().getContext(buildAuthenticatedDN(userName).toString(), password);
Control[] rctls = new Control[]{new PasswordPolicyControl(false)};
context.reconnect(rctls);
PasswordPolicyResponseControl ctrl = PasswordPolicyControlExtractor.extractControl(context);
//ctrl is always null
if (ctrl.isExpired()) {
throw new
PasswordPolicyException(ctrl.getErrorStatus());
}
What am I doing wrong? I am struggling greatly with this and any help would very much be appreciated.
If your client really sends the correct response control you might hit this issue (open since 7 years):
#1539 [RFE] Add code to check password expiration on ldap bind
IIRC FreeIPA enforces password expiry only during Kerberos pre-authc (kinit).

Spring Security no controller for login page

New at Spring Security here. I was looking at this link 'https://docs.spring.io/spring-security/site/docs/current/guides/html5/form-javaconfig.html#grant-access-to-remaining-resources' and got really stumped at the section Configuring a login view controller`.
When I'm creating a typical form, I usually make the html page that, on click, calls a method in my custom #controller, which sends to my logic, etc.
However, in their example, they state that no controller is needed because everything is 'default'. Can someone explain exactly how their login form can 'connect' to their authentication object? It looks like somehow the credentials can magically pass into the Authentication object despite having no controller method.
Thanks!
There is no controller. When you use the formLogin() method, a UsernamePasswordAuthenticationFilter is registred in the security filter chain and does the authentication job. You can look at the source code here:
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
Take again a look into https://docs.spring.io/spring-security/site/docs/current/guides/html5/form-javaconfig.html#configuring-a-login-view-controller. In the code snippet you can actually see, that an internal controller with the request mapping /login is registered. That is why you do not have to implement it on your own. All authentication transfer between view, internal controller and the authentication manager in the background is handled completely transparent to you.

Injecting Logged in User in Spring

Hi I want my user to be logged in via URL which is secured by spring. URL will contan username as well as password. I tried doing it by sending username and password via controller to customAuthenticationManager and then checked in CustomAuthentication Provider and returned UsernamePasswordAuthenticationToken. when I check isauthenticated flag it shows true but when I try to access a secured page it redirects me to the login page. Where am I going wrong ?
Its not the best way to do it but try this:
public void login(HttpServletRequest request, String userName, String password)
{
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password);
// Authenticate the user
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
// Create a new session and add the security context.
HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
}

Resources