How to fully logout? Spring Security - spring

How can I fully logout? I have in my xml file:
<security:logout logout-url="/logoutMe" />
And when I enter the /logoutMe url I am not logged out - on my controller I added:
SecurityContextHolder.getContext().getAuthentication().getName()
And added the name to ModelAndView and display on my page, and there is still my username, not anonymousUser as before logging in.
How to logout totally?
I've also tried to create my own LogoutSuccessHandler implementation with:
SecurityContextHolder.clearContext();
but.. it doesnt seem to work

This is probably a method which Spring security calls by default on logout(from SecurityContextLogoutHandler class):
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
Assert.notNull(request, "HttpServletRequest required");
if (invalidateHttpSession) {
HttpSession session = request.getSession(false);
if (session != null) {
logger.debug("Invalidating session: " + session.getId());
session.invalidate();
}
}
if(clearAuthentication) {
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(null);
}
SecurityContextHolder.clearContext();
}
have you tried to use default, provided by spring security logout url /logout? Or you have to customize some things?

Related

Authentication object is null after bypassing the particular request

The below Code I used in webConfigSecurity class to bypass some requests from the client
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity.ignoring().antMatchers("/adminSettings/get/**")
.antMatchers("/cases/sayHello/**").antMatchers("/cases/**/downloadPdfFolderPBC/**");
}
In the controller api method requires the user details for further execution, while getting the user details the authentication object is null, so it throws an exception that "user is not authenticated"
public static User get() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
if (principal == null) {
throw new InsufficientAuthenticationException("User not authenticated");
}
return principal.getUser();
}
throw new AuthenticationCredentialsNotFoundException("User not authenticated");
}
I'm new to spring security, In this case, what I should do to get logged user details
Instead of ignoring(), which causes Spring Security to skip those requests, I believe you want to use permitAll() instead like so:
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((requests) -> requests
.antMatcher("/adminSettings/get/**").permitAll()
.antMatcher("/cases/sayHello/**").permitAll()
// ... etc
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
}
In this way, Spring Security will still populate the SecurityContextHolder with the logged-in user, but none of the permitAll endpoints will require authentication.

How to allow original HttpServletRequest to handle login using Spring Boot Security

I am using Spring Security and I would like to handle authentication by the jee container.
So the only reason for spring security is authorization. All the pre authenticated filters are configured already and working.
I created a custom endpoint to trigger authentication.
import javax.http.HttpServletRequest;
...
#PostMapping("/auth/login")
public void login(HttpServletRequest request) throws ServletException {
request.login("something", "something");
}
...
It seems that HttpServletRequest is wrapped by SecurityContextHolderAwareRequestWrapper. The following method (which is provided by spring boot) handles authentication:
HttpServlet3RequestFactory$Servlet3SecurityContextHolderAwareRequestWrapper.login(...)
#Override
public void login(String username, String password) throws ServletException {
if (isAuthenticated()) {
throw new ServletException("Cannot perform login for '" + username
+ "' already authenticated as '" + getRemoteUser() + "'");
}
AuthenticationManager authManager = HttpServlet3RequestFactory.this.authenticationManager;
if (authManager == null) {
HttpServlet3RequestFactory.this.logger.debug(
"authenticationManager is null, so allowing original HttpServletRequest to handle login");
super.login(username, password);
return;
}
Authentication authentication;
try {
authentication = authManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password));
}
catch (AuthenticationException loginFailed) {
SecurityContextHolder.clearContext();
throw new ServletException(loginFailed.getMessage(), loginFailed);
}
SecurityContextHolder.getContext().setAuthentication(authentication);
}
If authManager is null it should trigger login from the original HttpServletRequest.
But that is the thing it isn't null because of the AnonymousAuthenticationProvider and PreAuthenticatedAuthenticationProvider attached to the ProviderManager. Is there another way to trigger login on its original HttpServletRequest?
I got around it by using my container servlet request in my method:
import com.ibm.ws.webcontainer.srt.SRTServletRequest;
#PostMapping("/auth/login")
public void login(SRTServletRequest request) throws ServletException {
request.login("something", "something");
}

Spring Boot application generate new SecurityContextHolder in every request

We have a tomcat application which works fine in IE7/8 and Firefox. The only browser we are having issues with (that we care about at this point) is google Chrome(above 47 version).
Users can navigate to the application fine and log in and do whatever they need to do in Firefox and IE. However, when trying to log in with Chrome, the session is apparently lost immediately after log in and when the authenticated user tries to navigate to another page they are bumped back to the log in page. This happens consistently.
I have looked at the JSESSIONID that is set in the cookie, which is sent back to mozila and IE on every request, while it does not for Chrome(above 47 version).
It's clear Spring SecurityContextHolder in which we set after the login. and take new SecurityContextHolder in every request.
here I am atteched my code where I am spring security is configure.
Any ideas are welcome!
We are using tomcat 8.0.33 and spring boot 4.2.4.RELEASE
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**","/restServiceFromKwh360/**","/cloudSSO/ssoLogin","/cloudSSO/ssoLogout","/cloudSSO/ssoCallback",
"/cloudSSO/ssoLoginError","/cloudSSO/ssoReturnFromKwh360Services").permitAll()
.antMatchers("/energyAudit/**").access("hasRole('ROLE_CUSTOMER_ADMIN')")
.and().formLogin().loginPage("/cloudSSO/ssoLogin").permitAll()
.and().exceptionHandling().accessDeniedPage("/cloudSSO/ssoLoginError")
.and()
.csrf().disable();
}
}
and here I am set Authentication object manually
#Override
public void AutoLoginUser(String username, HttpServletRequest request) {
try{
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication.getPrincipal() == "anonymousUser"){
User user;
try {
user = userDao.getUserByEmail(username);
} catch (KWH360DAOException e) {
e.printStackTrace();
logger.error("Request user is not registred with the system >>" + username);
throw new UsernameNotFoundException("You are not registered");
}
if (user == null){
logger.error("Request user is not registred with the system >>" + username);
throw new UsernameNotFoundException("You are not registered");
}
List<GrantedAuthority> authorities = buildUserAuthority(user.getRole().getName());
authentication = new UsernamePasswordAuthenticationToken(user,user.getPassword().toString(), authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
SecurityContextHolder.getContext());
}
}catch(Exception e){
e.printStackTrace();
}
}

Why to invalidate a session in a security filter, CSRF token is not updated

In a Spring Security application, a user is making a GET call to a private /secure url with activated csrf.
The user sends a credential cookie and then the user is authenticated and JSESSIONID is generated.
In the same browser becomes to make a GET call to /secure, with the JSESSIONID cookie and a credential cookie by another user.
In this case an owner security filter produces a change of user, invalid session of the first user, generate a
new session and authenticates the new credential.
This works until the new authenticated user sends a POST with the new JSESSIONID, but the CSRF token is getting to the old.
CsrfFilter is before my authentication filter, and CsrfAuthenticationStrategy not regenerate the CSRF token.
What is the right way to invalidate a session and the token csrf be updated?
The invalidation of session in spring security is automatic once you relogin as new user using the old JSESSION ID.
Basically you just have to bind the token generated on the new session generated by the authentication filter.
Try this solution, another filter after authentication
https://github.com/aditzel/spring-security-csrf-filter/blob/master/src/main/java/com/allanditzel/springframework/security/web/csrf/CsrfTokenResponseHeaderBindingFilter.java
Same issue reported via this thread,
https://github.com/aditzel/spring-security-csrf-token-interceptor/issues/1
I created a HttpSessionCsrfTokenRepository bean type, which is the default implementation of CsrfTokenRepository, and use both to configure spring security repository csrf as my filter, so the two sites use the same bean.
Config Bean
#Bean
public void CsrfTokenRepository dafaultCsrfTokenRepository() {
return new HttpSessionCsrfTokenRepository();
}
Spring Security
#Autowired
private CsrfTokenRepository csrfTokenRepository;
private static HttpSecurity addCSRF(HttpSecurity http, CsrfTokenRepository csrfTokenRepository) throws Exception {
http.csrf().csrfTokenRepository(csrfTokenRepository);
return http;
}
Invalidate session in filter
private void invalidateCurrentSession(HttpServletRequest request, HttpServletResponse response) {
SecurityContextHolder.clearContext();
HttpSession session = request.getSession(false);
if (session != null) {
CsrfToken csrfToken=csrfTokenRepository.loadToken(request);
session.invalidate();
request.getSession();
csrfTokenRepository.saveToken(csrfToken, request, response);
}
}
If someone wanted to override the default behavior, you could define a bean that implements CsrfTokenRepository as #Primary

Auto login after successful registration

hey all
i want to make an auto login after successful registration in spring
meaning:
i have a protected page which requires login to access them
and i want after registration to skip the login page and make an auto login so the user can see that protected page, got me ?
i am using spring 3.0 , spring security 3.0.2
how to do so ?
This can be done with spring security in the following manner(semi-psuedocode):
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
#Controller
public class SignupController
{
#Autowired
RequestCache requestCache;
#Autowired
protected AuthenticationManager authenticationManager;
#RequestMapping(value = "/account/signup/", method = RequestMethod.POST)
public String createNewUser(#ModelAttribute("user") User user, BindingResult result, HttpServletRequest request, HttpServletResponse response) {
//After successfully Creating user
authenticateUserAndSetSession(user, request);
return "redirect:/home/";
}
private void authenticateUserAndSetSession(User user, HttpServletRequest request) {
String username = user.getUsername();
String password = user.getPassword();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
// generate session if one doesn't exist
request.getSession();
token.setDetails(new WebAuthenticationDetails(request));
Authentication authenticatedUser = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
}
}
Update: to only contain how to create the session after the registration
In Servlet 3+ you can simply do request.login("username","password") and if successful, redirect to whatever page you want. You can do the same for auto logout.
Here is the link to the section of the documentation that talks about this: http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#servletapi-3
Just a comment to the first reply on how to autowire authenticationManager.
You need to set an alias when you declare authentication-manager in either your applicantion-servlet.xml or applicationContext-security.xml file:
<authentication-manager alias="authenticationManager>
<authentication-provider>
<user-service>
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
Also, when you authenticate, it may throw an AuthenticationException, so you need to catch it:
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword());
request.getSession();
token.setDetails(new WebAuthenticationDetails(request));
try{
Authentication auth = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(auth);
} catch(Exception e){
e.printStackTrace();
}
return "redirect:xxxx.htm";
Configure web.xml to allow Spring Security to handle forwards for a login processing url.
Handle registration request, e.g. create user, update ACL, etc.
Forward it with username and password to login processing url for authentication.
Gain benefits of entire Spring Security filter chain, e.g. session fixation protection.
Since forwards are internal, it will appear to the user as if they are registered and logged in during the same request.
If your registration form does not contain the correct username and password parameter names, forward a modified version of the request (using HttpServletRequestWrapper) to the Spring Security login endpoint.
In order for this to work, you'll have to modify your web.xml to have the Spring Security filter chain handle forwards for the login-processing-url. For example:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!-- Handle authentication for normal requests. -->
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Handle authentication via forwarding for internal/automatic authentication. -->
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/login/auth</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Source: mohchi blog
I incorporated the same scenario, below is the code snippet. To get the instance of AuthenticationManager, you will need to override the authenticationManagerBean() method of WebSecurityConfigurerAdapter class
SecurityConfiguration(extends WebSecurityConfigurerAdapter)
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Controller
#Autowired
protected AuthenticationManager authenticationManager;
#PostMapping("/register")
public ModelAndView registerNewUser(#Valid User user,BindingResult bindingResult,HttpServletRequest request,HttpServletResponse response) {
ModelAndView modelAndView = new ModelAndView();
User userObj = userService.findUserByEmail(user.getEmail());
if(userObj != null){
bindingResult.rejectValue("email", "error.user", "This email id is already registered.");
}
if(bindingResult.hasErrors()){
modelAndView.setViewName("register");
return modelAndView;
}else{
String unEncodedPwd = user.getPassword();
userService.saveUser(user);
modelAndView.setViewName("view_name");
authWithAuthManager(request,user.getEmail(),unEncodedPwd);
}
return modelAndView;
}
public void authWithAuthManager(HttpServletRequest request, String email, String password) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(email, password);
authToken.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = authenticationManager.authenticate(authToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
Using SecurityContextHolder.getContext().setAuthentication(Authentication) gets the job done but it will bypass the spring security filter chain which will open a security risk.
For e.g. lets say in my case when user reset the password, I wanted him to take to the dashboard without login again. When I used the above said approach, it takes me to dashboard but it bypassed my concurrency filter which I have applied in order to avoid concurrent login. Here is the piece of code which does the job:
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(empId, password);
Authentication auth = authenticationManager.authenticate(authToken);
SecurityContextHolder.getContext().setAuthentication(auth);
Use login-processing-url attribute along with a simple change in web.xml
security-xml
<form-login login-page="/login"
always-use-default-target="false"
default-target-url="/target-url"
authentication-failure-url="/login?error"
login-processing-url="/submitLogin"/>
web.xml
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/submitLogin</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
By adding this piece of code in web.xml actually does the job of forwarding your explicit forward request which you will make during auto login and passing it to the chain of spring security filters.
Hope it helps
This is an alternative to the Servlet 3+ integration. If you're using Spring Security's form login, then you can simply delegate to your login page. For example:
#PostMapping("/signup")
public String signUp(User user) {
// encode the password and save the user
return "forward:/login";
}
Assuming you have username and password fields in your form, then the 'forward' will send those parameters and Spring Security will use those to authenticate.
The benefit I found with this approach is that you don't duplicate your formLogin's defaultSuccessUrl (example security setup below). It also cleans up your controller by not requiring a HttpServletRequest parameter.
#Override
public void configure(HttpSecurity http) {
http.authorizeRequests()
.antMatchers("/", "/signup").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/home", true)
.permitAll();
}
Spring Monkey's answer works great but I encountered a tricky problem when implementing it.
My problem was because I set the registration page to have "no security", eg:
<http pattern="/register/**" security="none"/>
I think this causes no SecurityContext initialized, and hence after user registers, the in-server authentication cannot be saved.
I had to change the register page bypass by setting it into IS_AUTHENTICATED_ANONYMOUSLY
<http authentication-manager-ref="authMgr">
<intercept-url pattern="/register/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
...
</http>
This is answer to above question
In Controller:
#RequestMapping(value = "/registerHere", method = RequestMethod.POST)
public ModelAndView registerUser(#ModelAttribute("user") Users user, BindingResult result,
HttpServletRequest request, HttpServletResponse response) {
System.out.println("register 3");
ModelAndView mv = new ModelAndView("/home");
mv.addObject("homePagee", "true");
String uname = user.getUsername();
if (userDAO.getUserByName(uname) == null) {
String passwordFromForm = user.getPassword();
userDAO.saveOrUpdate(user);
try {
authenticateUserAndSetSession(user, passwordFromForm, request);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("register 4");
log.debug("Ending of the method registerUser");
return mv;
}
Further above method in controller is defined as:
`private void authenticateUserAndSetSession(Users user, String passwor`dFromForm, HttpServletRequest request){
String username = user.getUsername();
System.out.println("username: " + username + " password: " + passwordFromForm);
UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, passwordFromForm, userDetails.getAuthorities());
request.getSession();
System.out.println("Line Authentication 1");
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetails(request));
System.out.println("Line Authentication 2");
Authentication authenticatedUser = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
System.out.println("Line Authentication 3");
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
System.out.println("Line Authentication 4");
}
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());// creates context for that session.
System.out.println("Line Authentication 5");
session.setAttribute("username", user.getUsername());
System.out.println("Line Authentication 6");
session.setAttribute("authorities", usernamePasswordAuthenticationToken.getAuthorities());
System.out.println("username: " + user.getUsername() + "password: " + user.getPassword()+"authorities: "+ usernamePasswordAuthenticationToken.getAuthorities());
user = userDAO.validate(user.getUsername(), user.getPassword());
log.debug("You are successfully register");
}
Other answers didnt suggest to put it in try/catch so one does not realize why logic is not working as code runs...and nothing is there neither error or exception on console. So if you wont put it in try catch you wont get exception of bad credentials.
I'm not sure if you are asking for this, but in your Spring Security configuration you can add a "remember-me" tag. This will manage a cookie in your client, so next time (if the cookie hasn't expired) you'll be logged automatically.
<http>
...
<remember-me />
</http>

Resources