User sessions overlap in Spring MVC with Auth0 integration - spring

I am facing an issue with user sessions in Spring MVC java web app.
User sessions are overlapped
Ex:
Here we have 2 completely different users using different browsers, john#yahoo.com is using Chrome and sara#yahoo.com is using Edge. The behavior that we have noticed is that Sara's session in the Edge browser is overlapping John's session in Chrome... So sara is seeing John's profile in her browser window
Controller endpoint:
#RequestMapping(value = { "/" }, method = RequestMethod.GET)
public void loancenter(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
String redirectUri = config.getContextPath(request) + "/test/callback";
String authorizeUrl = authenticationController.buildAuthorizeUrl(request, response, redirectUri)
.withScope("openid name email family_name address phone_number user_id profile identities").build();
response.sendRedirect(authorizeUrl);
} catch (Exception e) {
LOG.info("Login page error");
response.sendRedirect(config.getContextPath(request) + "/test");
}
}
#RequestMapping(value = "/callback", method = RequestMethod.GET)
public void callback(HttpServletRequest request, HttpServletResponse response)
throws IOException, IdentityVerificationException {
try {
Tokens tokens = authenticationController.handle(request, response);
DecodedJWT jwt = JWT.decode(tokens.getIdToken());
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication auth = new UsernamePasswordAuthenticationToken(jwt.getSubject(), jwt.getToken(), grantedAuths);
request.setAttribute("email", jwt.getClaims().get("email").asString());
SecurityContextHolder.getContext().setAuthentication(auth);
response.sendRedirect(config.getContextPath(request) + "/test/home.do");
} catch (Exception e) {
LOG.info("callback page error");
response.sendRedirect(config.getContextPath(request) + "/test");
}
}
SecurityConfigurations:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.addFilterAfter(new MyCustomFilter(), UsernamePasswordAuthenticationFilter.class);
http.authenticationProvider(getAuthenticationProvider())
.authorizeRequests()
.antMatchers("/callback", "/", "/auth0/authorize", "/resources/**", "/public/**", "/static/**")
.permitAll()
.anyRequest()
.authenticated();
http.formLogin()
.successHandler(getUserLoginHandler());
http.logout()
.invalidateHttpSession(true)
.logoutSuccessUrl("/logout.do")
.addLogoutHandler(getUserLogoutHandler());
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.sessionFixation().newSession()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.expiredUrl("/login?invalid-session=true");
}
Can someone please help me to fix this issue?
Thanks in advance!

Related

JWT Interceptor Springboot

I'd like to make people who hold the JWT can access all APIs but people can only access on EXCLUDE PATH now. what should I set up for that?
This is my WebConfig.
private static final String[] EXCLUDE_PATHS = {
"/api/user/**"
};
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(EXCLUDE_PATHS);
This is my interceptor.
public class JwtInterceptor implements HandlerInterceptor {
private static final String HEADER_AUTH = "Authorization";
private final JwtTokenProvider jwtTokenProvider;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final String token = request.getHeader(HEADER_AUTH);
if(token !=null && jwtTokenProvider.validateToken(token)){
return true;
}else{
throw new UnauthorizedException();
}
this is my validateToken fn
public boolean validateToken(String jwtToken) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken);
return !claims.getBody().getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}
this is my doFilter
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) request);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
This is my security Config.
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf()
.ignoringAntMatchers("/h2-console/**")
.disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/h2-console/**").permitAll()
.antMatchers("/user/**").hasRole("USER")
.anyRequest().permitAll()
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
UsernamePasswordAuthenticationFilter.class);
}
Am I missing something? I add the Security Config.
You should use WebSecurity instead of interceptors.
Something like this for configuring which paths can be accessed and which cannot
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
This link should help you well.

Adding parameters to header does not work while implementing SpringBoot Security JWT for REST API

I'm trying to implement authentication and authorization using JWT token in SpringBoot REST API.
In my JWTAuthentication class
#Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res,
FilterChain chain, Authentication auth) throws IOException, ServletException {
String token = Jwts.builder().setSubject(((User) auth.getPrincipal()).getUsername())
.claim("roles", ((User) auth.getPrincipal()).getAuthorities())
.setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SecurityConstants.SECRET.getBytes()).compact();
res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
chain.doFilter(req, res);
System.out.println("Token:"+token);
}
When I test my code by sending by posting the following message to 127.0.0.1:8080/login URL, I see that authentication is successful.
{"username":"admin", "password":"admin"}
And then Spring calls my JWT Authorization class
#Override
protected void doFilterInternal(
HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws IOException, ServletException {
String header = req.getHeader(SecurityConstants.HEADER_STRING);
if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
if (header == null) {
System.out.println("header null");
} else if (!header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
System.out.println("token prefix missing in header");
}
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
It prints the message: "token prefix missing in header"
Although I add the TOKEN_PREFIX in the successfulAuthentication method, it can not find it in the header in doFilterInternal method.
By the way, my security config is like this:
#EnableWebSecurity(debug = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired private UserDetailsService userDetailsService;
#Autowired private BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/admin/**")
.hasRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(
"/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**");
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
I checked the SpringBoot books but could not find a book that describes the inner details of the security framework. Since I did not understand how the framework works, I could not solve the problems by just looking at the blogs. Is there a book that you can suggest describing the details of SpringBoot Security?
Thanks
You set your token after you successfully authenticated the user to the header of
the Http response:
res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
The internal JWT filter (from what I understand in your question is called after yours), looks in the Http headers of the request
String header = req.getHeader(SecurityConstants.HEADER_STRING);
and there they are not present.
In general, the second filter should not be active after you authenticated a user and should just return the JWT token to the client. Any subsequent call of the client should then include the JWT token in the Authorization header using Bearer: YourJWTToken for calling e.g. protected APIs.

why spring security hasRole function does not authenticate any apis

I have some trouble to make it work with spring security hasRole. I have 2 Role in db saved as ROLE_ADMIN and ROLE_USER. I want to give permisson some APIs with ADMIN role, some with USER role. HERE is my code.
SecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Value("${spring.queries.users-query}")
private String usersQuery;
#Value("${spring.queries.roles-query}")
private String rolesQuery;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder())
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery);
}
#Override
protected void configure(HttpSecurity http) {
System.out.println("configure " );
try {
http.csrf().disable().authorizeRequests()
.antMatchers("/", "/*.html").permitAll()
.antMatchers("/home").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/profile/").hasAnyRole("ADMIN","USER")
.antMatchers("/admin/*").hasRole("ADMIN")
.antMatchers("/insurance/*").hasRole("ADMIN")
.antMatchers("/company/*").hasRole("ADMIN")
.anyRequest().authenticated();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void configure(WebSecurity web) {
web.httpFirewall(allowUrlEncodedSlashHttpFirewall())
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/templates/**");
}
#Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
/*firewall.setAllowUrlEncodedSlash(true);
firewall.setAllowSemicolon(true);*/
firewall.setAllowUrlEncodedDoubleSlash(true);
return firewall;
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
And I have sql queries in application.properties
spring.queries.users-query=select username, password, status from insurance.users where username=?
spring.queries.roles-query=select u.username, r.role from insurance.users u inner join insurance.roles r on(u.role_id=r.id) where u.username=?
Problem is that when I try to login, I get 403 error code.
Here is Controller.class
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#RequestParam(value = "email") String email,
#RequestParam(value = "password") String password, HttpSession session) {
Result result = userService.login(email, password);
session.setAttribute("user", result.getData());
if(result.getStatus() == 200){
return "redirect:/profile";
} else {
return "redirect:/login?error";
}
}
#RequestMapping(value = "/profile", method = RequestMethod.GET)
public String profile(HttpSession httpSession, Model model) {
if(httpSession.getAttribute("user") != null) {
UserResponse user = (UserResponse) httpSession.getAttribute("user");
model.addAttribute("user", user);
return "profile";
} else {
return "redirect:/home";
}
}
I have tried to solve it, but could not find. If you have any advice, please tell.
I changed my config file as suggested. I added my custom login logic, now when I want to go /admins or another url, I redirect to login url Here is my config code
protected void configure(HttpSecurity http) {
try {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/home").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/insurance/*").hasRole("ADMIN")
.antMatchers("/company/*").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/login.html").permitAll().usernameParameter("username") .passwordParameter("password")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/profile.html", true);
} catch (Exception e) {
e.printStackTrace();
}
}
Just add login also in exceptions for authentication.
Give this a try
protected void configure(HttpSecurity http) {
try {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/home").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/insurance/*").hasRole("ADMIN")
.antMatchers("/company/*").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/login.html").permitAll().usernameParameter("username") .passwordParameter("password")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/profile.html", true);
} catch (Exception e) {
e.printStackTrace();
}
}

Spring swichUserFilter redirects to target Url without switching

I have some data that i need to replicated for userA.
As i dont know userA's password, i want to login as adminUser & switch to userA & post the data. Related to this i have two questions :-
Question 1) I am first trying to login & switch using the example given in the response here How to impersonate user using SwitchUserFilter in Spring?
private final TokenProvider tokenProvider;
protected UserDetailsService userDetailsService;//= (UserDetailsService) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
private final CorsFilter corsFilter;
private final SecurityProblemSupport problemSupport;
public SecurityConfiguration(UserDetailsService userDetailsService,TokenProvider tokenProvider, CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
this.tokenProvider = tokenProvider;
this.corsFilter = corsFilter;
this.userDetailsService = userDetailsService;
this.problemSupport = problemSupport;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(switchUserFilter(), FilterSecurityInterceptor.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/account/reset-password/init").permitAll()
.antMatchers("/api/account/reset-password/finish").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/login/switchUser").permitAll()
.antMatchers("/login/impersonate").permitAll()
.and()
.apply(securityConfigurerAdapter());
// #formatter:on
}
#Bean
public SwitchUserFilter switchUserFilter() {
SwitchUserFilter filter = new SwitchUserFilter();
filter.setUserDetailsService(userDetailsService);
filter.setSwitchUserUrl("/login/impersonate");
filter.setSwitchFailureUrl("/login/switchUser");
filter.setTargetUrl("/#/home");
return filter;
}
private JWTConfigurer securityConfigurerAdapter() {
return new JWTConfigurer(tokenProvider);
}
}
What i have tried is, i logged in as adminUser and in the url i try to switch by changing the url to http://localhost:9000/login/impersonate?username=userA
Now, my issue is i get successfully redirected to the home screen but my user remains adminUser. (i do this cause, when i make a get/post call from postman i get response saying browser is outdated & need to enable javascript)
P.S. :- I have a jhipster developed application, so most of the classes are already added by default.
P.P.S. :- I know i'm extremely dumb
Question 2) As i mentioned earlier, i need to replicate the data & i need to do it programatically, how can i achieve this ? can SwitchUserFilter call a rest url & pass some custom data/values to it ?
add this custom method in UserJwTController
#PostMapping("/authenticate-externalnodes")
public ResponseEntity<JWTToken> authenticateExternalnodes(#Valid #RequestBody LoginVM loginVM) {
// Get Roles for user via username
Set<Authority> authorities = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername()).get()
.getAuthorities();
// Create Granted Authority Rules
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (Authority authority : authorities) {
grantedAuthorities.add(new SimpleGrantedAuthority(authority.getName()));
}
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
loginVM.getUsername(), "", grantedAuthorities);
Authentication authentication = authenticationToken;
SecurityContextHolder.getContext().setAuthentication(authentication);
boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
String jwt = tokenProvider.createToken(authentication, rememberMe);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
}

spring boot + security + jquery ajax

Hi,I build a project with spring boot & spring security. Now, I want to provider the login restfull service for Jquery.ajax({...}); And I want to:
process the login request from HTML page (like the <form> submit).
automatic to check session timeout when HTML page request, redirect timeout to login page.
process the login request from Ajax.
automatic to check the login state when Ajax request.
I coding like this
SecurityConfig
extends from WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
String requestType = request.getHeader("x-requested-with");
if (!StringUtils.isEmpty(requestType)) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().print("{\"invalid_session\": true}");
response.getWriter().flush();
} else {
response.sendRedirect("/security/login");
}
});
http.authorizeRequests()
.antMatchers("/security/**").permitAll()
.antMatchers("/reader/**").hasRole("READER")
.anyRequest().authenticated()
// session time out
.and().sessionManagement().invalidSessionUrl("/security/session_timeout")
.and().cors()
// login
.and()
.formLogin()
.successHandler(successHandler)
.failureHandler(faildHandler)
.loginPage("/security/login")
.permitAll()
// logout
.and()
.logout().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(readerRepository::findOne);
}
I have two handlers to process AuthenticationSuccess and AuthenticationFailure.
FailureHandler
extends from SimpleUrlAuthenticationFailureHandler
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
System.out.println("Failed to auth.");
String requestType = request.getHeader("x-requested-with");
if (!StringUtils.isEmpty(requestType)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().print("{\"success\": false}");
} else {
setDefaultFailureUrl("/security/login?error=true");
super.onAuthenticationFailure(request, response, exception);
}
}
SuccessHandler
extends from SavedRequestAwareAuthenticationSuccessHandler
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println("Success to auth.");
String requestType = request.getHeader("x-requested-with");
if (!StringUtils.isEmpty(requestType)) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().print("{\"success\": true}");
} else {
setDefaultTargetUrl("/index/index");
setAlwaysUseDefaultTargetUrl(true);
super.onAuthenticationSuccess(request, response, authentication);
}
}
Controller
base RequestMapping is '/security'
#RequestMapping(value = "/login")
public String login(#RequestParam(value = "error", defaultValue = "false") boolean error, Model model) {
model.addAttribute("error", error);
return "login";
}
#RequestMapping("/session_timeout")
public void sessionTimeout(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("session was timeout.");
if (request.getHeader("x-requested-with") != null) {
// handler for ajax
response.getWriter().print("{\"sessionTimeout\": true}");
response.getWriter().close();
} else {
response.sendRedirect("login");
}
}
When I test in page(thymeleaf), All of worked.
but.. when I use Jquery Ajax.
The issure:
When I used Jquery.ajax({}) API to send the request, the request can not be get to server. How to write ajax request with jquery, I tried a lot of Jquery methods, page has no response code in console. Is the spring security not support ajax?
Thanks Fan, I fixed it. I rewrote the login authentication:
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDetails, userReq.getPassword(), userDetails.getAuthorities());
authenticationManager.authenticate(token);
if (token.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(token);
return true;
}
Do auth with AuthenticationManager and it injected from spring.
If success to auth, I will return the sessionid to the client and client saved in cookie, when client do request, the client always stats sessionid at the end of the url requested by ajax.
If failed to auth, I wile return agreed error code.
eg:
$.ajax({
url: 'http://test:port/project/list;jsessionid=' + jessionid,
...
})
But I do not think so is good job like this. It's very troublesome, In the client,I need to check every response code is it right or not for every request. Is any better ways to solve this ensure??
by the way, the Client(Browser + Ajax) and Server(Spring mvc) are separate.

Resources