Spring Security: Unable to re-login - spring

I am fixing an existing application, where first login works fine and the logout works fine taking me back to login page.
However after logout, when I try to re-login without browser refresh (just enter credentials on login page), I am getting redirected to Host based URL(which is blocked over the intranet).
Reason
SavedRequest savedRequest = requestCache.getRequest(request, response);
is null during re-login.
I think that may be because I loggedOut, session is cleared by LogoutHandler.
Code for reference.
`public class BroadleafAdminAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
RequestCache requestCache = new HttpSessionRequestCache();
private static final String successUrlParameter = "successUrl=";
private static final String APPLICATIONURL = "APPLICATIONURL";
#Resource(name = "blAdminSecurityRemoteService")
protected SecurityVerifier adminRemoteSecurityService;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
AdminUser user = adminRemoteSecurityService.getPersistentAdminUser();
if (user != null && user.getLastUsedSandBoxId() != null) {
request.getSession(false).setAttribute(BroadleafSandBoxResolver.SANDBOX_ID_VAR, user.getLastUsedSandBoxId());
}
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
.
String targetUrl = savedRequest.getRedirectUrl();
.
*Logic where I update my targetUrl*
.
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
}`
I want to avoid going into this condition
if (savedRequest == null) Since I am then unable to execute
*Logic where I update my targetUrl*

Related

I have implemented JWT token security in spring boot code. how will I get jwt token anywhere in my code? need to save audit

I have implemented jwt security token in spring boot by refering jwt security impemented videos.
So after login I get generated jwt token, For further end points to hit I need to pass jwt token from request header then re request will get authorize at dofilter() method in JwtAuthenticationTokenFilter class as shown below.
public class JwtAuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Value("${jwt.header}")
private String tokenHeader;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String username = null;
String authToken = null;
HttpServletRequest httpRequest = (HttpServletRequest) request;
String header = httpRequest.getHeader(this.tokenHeader);
if (header != null && header.startsWith("Bearer ")) {
authToken = header.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(authToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}
But I need to get that jwt token anywhere i want in my code to get some data from token.
for example look below code
public static AuditDetails createAudit() {
AuditDetails auditDetails = new AuditDetails();
**auditDetails.setCreateUser(token.getUsername());**
auditDetails.setCreateTime(new Date());
return auditDetails;
}
so basically i need to get username from token to same audit details, but how am i suppose get token in that code or anywhere in the code?
The token is sent to your app via the header (tokenHeader)
Edit
If you do not want to use the content of your HttpServletRequest anywhere, you can use as per session, a value holder that you can Inject (autowire) in every service to utilize the submitted token. You can try the following
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyHolder {
private String authToken;
public String getAuthToken() {
return authToken;
}
public void setAuthToken(String authToken) {
this.authToken = authToken;
}
}
Change the token value in your JwtAuthenticationTokenFilter
#Autowired MyHolder myHolder;
// ...
String authToken = null;
HttpServletRequest httpRequest = (HttpServletRequest) request;
String header = httpRequest.getHeader(this.tokenHeader);
if (header != null && header.startsWith("Bearer ")) {
authToken = header.substring(7); // Here is your token
// UPDATE THE TOKEN VALUE IN YOUR HOLDER HERE
myHolder.setAuthToken(authToken);
// ...
}
Access the token anywhere in your app by autowiring the MyHolder class
#Autowired MyHolder myHolder;
// ...
var token = myHolder.getAuthToken();

Spring Security 5 Stateless OAuth2 Login - how to implement cookies based AuthorizationRequestRepository

I'm trying to have Google/Facebook login using Spring Security 5 OAuth2 login feature. But the problem I'm facing is that I'm coding a stateless API, whereas Spring security 5 uses HttpSessionOAuth2AuthorizationRequestRepository to store authorization requests, which uses session. So, I thought not to use that and code a cookie based implementation, which looks as below:
public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
private static final String COOKIE_NAME = "some-name";
#Override
public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
Assert.notNull(request, "request cannot be null");
return fetchCookie(request)
.map(this::toOAuth2AuthorizationRequest)
.orElse(null);
}
#Override
public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request,
HttpServletResponse response) {
Assert.notNull(request, "request cannot be null");
Assert.notNull(response, "response cannot be null");
if (authorizationRequest == null) {
deleteCookie(request, response);
return;
}
Cookie cookie = new Cookie(COOKIE_NAME, fromAuthorizationRequest(authorizationRequest));
cookie.setPath("/");
cookie.setHttpOnly(true);
response.addCookie(cookie);
}
private String fromAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest) {
return Base64.getUrlEncoder().encodeToString(
SerializationUtils.serialize(authorizationRequest));
}
private void deleteCookie(HttpServletRequest request, HttpServletResponse response) {
fetchCookie(request).ifPresent(cookie -> {
cookie.setValue("");
cookie.setPath("/");
cookie.setMaxAge(0);
response.addCookie(cookie);
});
}
#Override
public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request) {
// Question: How to remove the cookie, because we don't have access to response object here.
return loadAuthorizationRequest(request);
}
private Optional<Cookie> fetchCookie(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0)
for (int i = 0; i < cookies.length; i++)
if (cookies[i].getName().equals(COOKIE_NAME))
return Optional.of(cookies[i]);
return Optional.empty();
}
private OAuth2AuthorizationRequest toOAuth2AuthorizationRequest(Cookie cookie) {
return SerializationUtils.deserialize(
Base64.getUrlDecoder().decode(cookie.getValue()));
}
}
The above basically stores the data in a cookie instead of session. I've a couple of questions:
How exactly to code the removeAuthorizationRequest method above? I wanted the cookie removed there, but we don't have access to the response object.
Does the above (cookie based) approach look okay? E.g. any security issues?
Update: Have created an issue at https://github.com/spring-projects/spring-security/issues/5313 . Until that's addressed, here is a workaround I came up with: https://www.naturalprogrammer.com/blog/1681261/spring-security-5-oauth2-login-signup-stateless-restful-web-services

Session attributes not working in Spring #RestControllers

I have added location as a session attribute as given below
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
com.blife.werp.model.User userSession = userRepository.findByUsername(user.getUsername());
HttpSession session = request.getSession();
session.setAttribute("user", userSession);
session.setAttribute("username", user.getUsername());
session.setAttribute("location",userSession.getLocation());
try {
userService.isloggedIn(true, userSession,request);
} catch (Exception e) {
e.printStackTrace();
}
redirectStrategy.sendRedirect(request, response, "/dashboard");
}
but when I used it in Spring Rest service as given bellow gave me and exception
"org.hibernate.LazyInitializationException: could not initialize proxy - no Session"
#RequestMapping("/get_product_by_location")
public List<ProductStock> getProductByLocation(HttpServletRequest request, #RequestParam String code){
HttpSession session = request.getSession();
Location location = session != null ? (Location) session.getAttribute("location") : null;
System.out.println(code+" "+location);
List<ProductStock> products = productService.getProductByLocation(code,location);
System.out.println(products);
return products;
}
Can any one let me now the issue in my code example to cause this error, If the "location" attribute is already in the session why it gave me an Lazy Initialization I am not going fetch this via any Repositories.

How to know that a session is expired?

I set values to the session object in the method of a controller after success of login :
#RequestMapping(value = "/", method = RequestMethod.POST)
public ModelAndView processLogin(Model model, HttpServletRequest request, HttpSession session, #RequestParam String login, #RequestParam String pwd) {
if ( utilisateurDao.verifierLoginUser(login) ) {
if ( utilisateurDao.verifierUser(login, pwd) ) {
HashMap<String, String> criteres = new HashMap<String, String>();
criteres.put("user_login", login);
criteres.put("user_passwd", pwd);
List<Utilisateur> users = utilisateurDao.lireParCritere(criteres);
session.setAttribute("user_code", ((Utilisateur)users.get(0)).getUser_code());
session.setAttribute("menu", menuDao.afficherMenuParUtilisateur((Integer)session.getAttribute("user_code"), env, request, session));
criteres.clear();
users.clear();
criteres.put("user_code", String.valueOf(session.getAttribute("user_code")));
users = utilisateurDao.lireParCritere(criteres);
session.setAttribute("user_names", ((Utilisateur)users.get(0)).getNoms());
session.setAttribute("logout_menu", env.getProperty("menu.logout"));
return new ModelAndView("redirect:/accueil");
} else {
ModelAndView modelViewLogin = new ModelAndView("redirect:/");
modelViewLogin.addObject("e", "p").addObject("l", login);
return modelViewLogin;
}
} else {
ModelAndView modelViewLogin = new ModelAndView("redirect:/");
modelViewLogin.addObject("e", "l");
return modelViewLogin;
}
}
Then I opened the app inactive for some minutes. After that I went to the "accueil" path. Then the menu was not shown anymore ! The menu was got from session. So how to know that the session is expired and where is the convenient place to test it ?
By default in spring security session is stored in SessionRegistry.
By using SecurityContext you can get this info in your controller code.
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
If you want to be notified when session has expired or person logged out you can always register listener on SessionDestroyedEvent- documentation.
example:
#Component
public class LogoutListener implements ApplicationListener<SessionDestroyedEvent> {
#Override
public void onApplicationEvent(SessionDestroyedEvent event) {
//do your stuff here
}
}
Its also worth to refer to spring docs for that subject.
You can make a Interceptor,
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter
In this interceptor you can control the HttpServletRequest
and check if obj exists into them and then you can throw to a new SessionExpiredException and catch with #ExceptionMapper (https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc)
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (request.getSession().getAttribute("user")==null) {
throw new SessionExpiredException();
}
return true;
}
I check like below. I think it might be help.
public boolean isUserLoggedIn(HttpServletRequest request) throws IOException {
SecurityContext securityContext = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
if(securityContext != null) {
Authentication authentication = securityContext.getAuthentication();
if(null != authentication && authentication.isAuthenticated() != true)
return false;
else
return true;
} else {
return false;
}
}

Spring security - Restricting Authenticated User redirection to Login

After login, when login url is accessed with out logging out, login page is shown, but I do not want the login Page, instead remain on the same page even when login url is accessed from address bar.
Following is my security configuration:
<form-login login-page="/loginform.do" authentication-failure-url = "/loginform.do?error=1" default-target-url="/dashBoard.do" always-use-default- target="false" />
One solution I come across is to redirect page, if the role is not 'ROLE_ANONYMOUS'
<sec:authorize ifNotGranted="ROLE_ANONYMOUS">
<% response.sendRedirect("/mainpage.jsp"); %>
</sec:authorize>
But can a similar configuration be done in security configuration file ?
I solved this with an HandlerInterceptor because I dont know a build in solution.
import org.springframework.web.util.UrlPathHelper;
...
public class PreventLoginPageForLoggedInUserInterceptor extends HandlerInterceptorAdapter {
private UrlPathHelper urlPathHelper = new UrlPathHelper();
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler)
throws Exception {
if (urlPathHelper.getLookupPathForRequest(request).startsWith("/login"))
&& isAuthenticated()) {
sendRedirect(request, response);
return false;
} else {
return true;
}
}
private void sendRedirect(HttpServletRequest request,
HttpServletResponse response) {
response.setStatus(HttpStatus.TEMPORARY_REDIRECT.value());
response.setHeader("Location", response.encodeRedirectURL(request.getContextPath() + "/"));
}
private boolean isAuthenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return (authentication != null)
&& (!authentication instanceof AnonymousAuthenticationToken)
&& authentication.isAuthenticated()
}
}

Resources