AngularJS and Spring MVC security - java

I have a front end written in AngularJS, and a Spring MVC backend. The idea I had was to only secure the REST API services and use an interceptor in AngularJS to redirect the user to the login page when an unauthorized service call is made. The problem I'm facing now is that, while a service is called, the page is briefly displayed before the user is redirected. Is there anything I can do about that? Or is this approach fundamentally flawed?
This is the interceptor:
$httpProvider.interceptors.push(function ($q, $location) {
return {
'responseError': function(rejection) {
var status = rejection.status;
if (status == 401 || status == 403) {
$location.path( "/login" );
} else {
}
return $q.reject(rejection);
}
};});
My security configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().authenticated();
}
#Bean(name="myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
The login controller:
#RequestMapping(value = "/login", method = RequestMethod.POST, produces="application/json")
#ResponseBody
public String login(#RequestBody User user) {
JSONObject result = new JSONObject();
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
try {
Authentication auth = authenticationManager.authenticate(token);
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(auth);
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
result.put("isauthenticated", true);
} catch (BadCredentialsException e) {
result.put("isauthenticated", false);
}
return result.toString();
}

I think this approach is OK, but you may have to live with the page flash, which in turn means you will have to handle it gracefully.
I guess the page flush happens roughly as follows:
A navigation takes place, rendering a template and activating a controller for the new route
The controller calls a service; this is asynchronous, so the page without any data is displayed
The service returns 401/403, it is intercepted and the new navigation to the login page occurs
You may want to try:
Collecting all data required by the page in the resolve configuration of the route (supported both by ngRoute and angular-ui-router), so that the navigation will not complete before all data is fetched.
Handle it from within the page: while the service call is still pending, display a spinner/message whatever to let the user know that some background activity is going on.
When the interceptor catches a 401/403, have it open a modal popup, explaining the situation and offering the user to login or navigate to the login page as a single option. Combine this with the spinner/message.

Related

Updated password not picked up after InMemoryUserDetailsManager updateUser call

Hi I have a Rest WS using WebSecurityConfigurerAdapter to implement HTTP Basic auth.
The password is allowed to be updated and I need to let the WS to pick up updated password without restarting server
Following are the codes:
SecurityConfig
// init a user with credentials admin/password
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//disable csrf
.csrf().disable()
//authentic all requests
.authorizeRequests().anyRequest().authenticated().and().httpBasic()
//disable session
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(inMemoryUserDetailsManager());
}
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
Properties users = new Properties();
users.put("admin", "password,USER,enabled");
return new InMemoryUserDetailsManager(users);
}
}
The controller that will update password
#RestController
public class someController{
#Autowired
public InMemoryUserDetailsManager inMemoryUserDetailsManager;
// update password from password -> pass
#RequestMapping(...)
public updatePass(){
ArrayList<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
grantedAuthoritiesList.add(new SimpleGrantedAuthority("USER"));
this.inMemoryUserDetailsManager.updateUser(new User("admin", "pass", grantedAuthoritiesList));
}
// another way that also doesn’t work
#RequestMapping(...)
public newUpdate(){
ArrayList<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
grantedAuthoritiesList.add(new SimpleGrantedAuthority("USER"));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("admin", "pass",
grantedAuthoritiesList);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
}
}
After calling updatePass() with credential admin/password for the first time, I can see that the password has been updated to "pass" in debugger
I assume that if I'm to call updatePass() again, I should use admin/pass. However it turned out to be still using the old admin/password.
Sources I referred to when writing this code source1 source2
*I'm using Advance Rest Client to make the calls
When you update the password, you have to set the UserDetails in springSecurityContext object if the user is authenticated.
instead of using SecurityContext, I overwrote function loadUserByUsername of interface UserDetailsService to let spring security always pick up the latest pwd from DB.

Spring Security authenticate user via post

I have a react app running on a separate port (localhost:3000) that i want to use to authenticate users with, currently i have a proxy setup to my Spring backend (localhost:8080).
Can I somehow manually authenticate instead of http.httpBasic() by sending a POST request to my backend and getting back a session cookie then include the cookie with every request? It would simplify the auth process on iOS side aswell (using this process i could only store the session cookie value in keychain and pass it with every request made to my api)
How would I disable csrf for non-browser requests?
Is there a better approach to this? Diffrent paths for browser and mobile auth?
{
"username": "user",
"password": "12345678"
}
Handle the request in spring controller
#PostMapping(path = "/web")
public String authenticateUser() {
//Receive the auth data here... and perform auth
//Send back session cookie
return "Success?";
}
My WebSecurityConfigurerAdapter.java
#Configuration
#EnableWebSecurity
public class WebsecurityConfig extends WebSecurityConfigurerAdapter {
private final DetailService detailService;
public WebsecurityConfig(DetailService detailService) {
this.detailService = detailService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(detailService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST,"/api/v1/authenticate/new").permitAll()
.antMatchers(HttpMethod.POST,"/api/v1/authenticate/web").permitAll()
.anyRequest().authenticated();
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST").allowedOrigins("http://localhost:8080");
}
};
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(14);
}
}
You can create an endpoint that takes user's credentials in a request body, perform authentication and then set tokens, and other required parameters in HttpOnly cookies.
After setting cookies, subsequent requests can read access/refresh token from cookies and add it in requests, you can then use custom CheckTokenEndpoint to verify tokens.
In the following example TokenParametersDto is a POJO that has username and password properties.
For issuing token (by verifying credentials) you can delegate call to TokenEndpoint#postAccessToken(....) or use its logic to your own method.
#PostMapping(path = "/oauth/http/token", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> issueToken(#RequestBody final #Valid #NotNull TokenParametersDto tokenParametersDto,
final HttpServletResponse response) {
final OAuth2AccessToken token = tokenService.issueToken(tokenParametersDto);
storeTokenInCookie(token, response);
return new ResponseEntity<>(HttpStatus.OK);
}
private void storeTokenInCookie(final OAuth2AccessToken token, final HttpServletResponse response) {
final Cookie accessToken = new Cookie("access_token", token.getValue());
accessToken.setHttpOnly(true);
accessToken.setSecure(sslEnabled);
accessToken.setPath("/");
accessToken.setMaxAge(cookieExpiration);
final Cookie tokenType = new Cookie("token_type", token.getTokenType());
tokenType.setHttpOnly(true);
tokenType.setSecure(sslEnabled);
tokenType.setPath("/");
tokenType.setMaxAge(cookieExpiration);
// Set Refresh Token and other required cookies.
response.addCookie(accessToken);
response.addCookie(tokenType);
}
Check this answer for disabling CSRF for a specific URL section.

How to invalidate remember-me on logout?

Well, I do not implement PersistentTokenBasedRememberMeServices therefore I cannot use .logout(request, response, auth). But I use JdbcTokenRepositoryImpl in order to use PersistentTokenRepository for remember-me feature.
LogoutController:
#Controller
public class LogoutController {
#RequestMapping(value = {"/logout"}, method = RequestMethod.GET)
public String logout() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth != null) {
SecurityContextHolder.getContext().setAuthentication(null);
}
return "redirect:/login?logout";
}
}
Security config:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/playground").hasAnyRole("ROOT", "MODER", "USER")
.antMatchers("/users/**").hasAnyRole("ROOT", "MODER")
.and()
.formLogin().loginPage("/login").loginProcessingUrl("/login").failureHandler(customAuthenticationFailureHandler())
.and()
.rememberMe().rememberMeParameter("remember-me").tokenRepository(persistentTokenRepository()).userDetailsService(userDetailsService)
.and()
.logout().logoutUrl("/logout");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setPasswordEncoder(passwordEncoder());
authProvider.setUserDetailsService(userDetailsService);
return authProvider;
}
#Bean
public AuthenticationFailureHandler customAuthenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
#Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
When I log in with remember-me, I cannot log out then. I guess because of remember-me feature. What should I add to LogoutController to make a proper logout proccess?
Note: the thing is, that if I just use POST method on logout, then it perfectly works, but I'd like to use GET method and thus I have to create a logout controller to perform get method.
Try to disable crsf (http.csrf().disable()).
The default implementation in spring's security Logout filter is:
if (http.getConfigurer(CsrfConfigurer.class) != null) {
this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
}
else {
this.logoutRequestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(this.logoutUrl, "GET"),
new AntPathRequestMatcher(this.logoutUrl, "POST"),
new AntPathRequestMatcher(this.logoutUrl, "PUT"),
new AntPathRequestMatcher(this.logoutUrl, "DELETE")
);
}
as you can see if your Csrf is enabled (by default it's enabled even if you overwride protected void configure(HttpSecurity http)) then only POST method will be work, if not all are working.
BTW: Are you sure your request reaching the LogoutController, because I thing it's uses standard spring security logout mechanism? (To disable it do http.logout().disable(), the same as csrf it's enabled by default)
To sum it up.
I've managed to test several ways and here what I have got:
As M. Deinum suggested in comments, it's possible not to use a controller and nevertheless have a logout with a GET request. Here it is.
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
As Andrew Sasha suggested in the answer section, you can disable csrf as it intentionally prevents using GET request. And now you can use a GET request to log out without even using any controller.
http.csrf().disable()
If you still wish to use the controller, none of the following things will help you
.deleteCookies("remember-me", "JSESSIONID")
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/")
(I'm not sure, but I feel like it doesn't work because you perform a GET request and use your controller to control logout)
So then you can do it programmatically
First, you add a name for remember me cookie in Spring security config:
rememberMe().rememberMeCookieName("remember-me")
And then in logout controller add this:
String cookieName = "remember-me";
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
response.addCookie(cookie);
The only problem here is that you have to delete a record from persistent_logins table manually
(In order to get a request and a response you just pass them into a method public void logout(HttpServletRequest request, HttpServletResponse response)
It is possible to use a POST request but use it as a link with the help of JavaScript or even plain HTML and CSS.
The solutions for that you can find on this topic.
So what do we have here?
Summarizing everything above I can say that if you want a controller, you have to programmatically write everything yourself (someone would tell that it's a reinventing of a wheel).
Still, it is possible to use a GET request but without controller which is described in the 1st and 2nd positions of the list.
(The consequences of using a GET request is written within CSRF Documentation and it does not recommend to use a GET request because of its invulnerability.)
So the last thing that I decided to be my favorite is to make a POST request look like a GET request (use it as a link) with the help of JS or HTML and CSS. And as you use a POST request you kind of have a CSRF protection.
I hope this will help someone.

can not call auth provider for second time hit

I am using spring security for authentication
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider).authenticationProvider(secondaryAuthProvider) ;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/login").hasAnyRole("ADMIN","VISITOR").and().
formLogin().defaultSuccessUrl("/login").failureUrl("/")
.loginPage("/login").usernameParameter("username").passwordParameter("password").failureUrl("/").
and().logout().permitAll().and().exceptionHandling().accessDeniedPage("/403").and()
.authorizeRequests().antMatchers("/resources/**").permitAll().and().authorizeRequests().
antMatchers("/api/**").authenticated().and().httpBasic().realmName("MY_TEST_REALM").
authenticationEntryPoint(getBasicAuthEntryPoint());
}
#Bean
public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint(){
return new CustomBasicAuthenticationEntryPoint();
}
This is working fine. When i hit /api/login i am able to use basic authentication
But after first successful authentication I am able to use /api/login without authentication.
It is not taking me to auth provider at second time. First time control is going there but not second time.
Register two WebSecurity configurations:
#Configuration
#EnableWebSecurity
#Order(1)
public class StatefulConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider).authenticationProvider(secondaryAuthProvider) ;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).and()
.antMatcher("/web/*").authorizeRequests()
.antMatchers("/*").hasAnyRole("ADMIN","VISITOR").and().
formLogin().defaultSuccessUrl("/web/login").failureUrl("/web/error").loginPage("/web/login").usernameParameter("username").passwordParameter("password").failureUrl("/").
and().logout().logoutUrl("/web/logout").permitAll().and().exceptionHandling().accessDeniedPage("/403").and()
.authorizeRequests().antMatchers("/resources/**").permitAll();
}
}
And for rest:
#Configuration
#Order(2)
public class StatelessConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider).authenticationProvider(secondaryAuthProvider) ;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.antMatcher("/api/*").authorizeRequests()
.antMatchers("/api/**").authenticated().and().httpBasic().realmName("MY_TEST_REALM").
authenticationEntryPoint(getBasicAuthEntryPoint());
}
#Bean
public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint(){
return new CustomBasicAuthenticationEntryPoint();
}
}
Be careful: there are antMatcher(...) and antMatchers(...) methods.
UPDATE: similar problem & solution here
Session is created when you log in. Session will be active until you logout (destroy session), or when time expire.
See example
EDIT:
Spring application have a few important settings associated with session.
The first one is session creation policy (by default IF_REQUIRED - if session linked with request already exists it will be not destroyed and created again).
Session is saved in cookie - you can check it hitting f12.
Application "check" does cookie exist in request. When you go to login page there are two cases:
you don't have session -> login popup appears, you can log in,
you have session because SecurityContextHolder contain information about current session.
How does it work?
When you use .httpBasic(), Spring Security registers BasicAuthenticationFilter. In method doFilterInternal you can see:
if (authenticationIsRequired(username)) {
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, tokens[1]);
authRequest.setDetails(
this.authenticationDetailsSource.buildDetails(request));
Authentication authResult = this.authenticationManager
.authenticate(authRequest);
if (debug) {
this.logger.debug("Authentication success: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
this.rememberMeServices.loginSuccess(request, response, authResult);
onSuccessfulAuthentication(request, response, authResult);
}
After success first login, authentication is set.
When you try to log in again authenticationIsRequired method returns false. Why?
Look at the source:
private boolean authenticationIsRequired(String username) {
// Only reauthenticate if username doesn't match SecurityContextHolder and user
// isn't authenticated
// (see SEC-53)
Authentication existingAuth = SecurityContextHolder.getContext()
.getAuthentication();
if (existingAuth == null || !existingAuth.isAuthenticated()) {
return true;
}
// Limit username comparison to providers which use usernames (ie
// UsernamePasswordAuthenticationToken)
// (see SEC-348)
if (existingAuth instanceof UsernamePasswordAuthenticationToken
&& !existingAuth.getName().equals(username)) {
return true;
}
// Handle unusual condition where an AnonymousAuthenticationToken is already
// present
// This shouldn't happen very often, as BasicProcessingFitler is meant to be
// earlier in the filter
// chain than AnonymousAuthenticationFilter. Nevertheless, presence of both an
// AnonymousAuthenticationToken
// together with a BASIC authentication request header should indicate
// reauthentication using the
// BASIC protocol is desirable. This behaviour is also consistent with that
// provided by form and digest,
// both of which force re-authentication if the respective header is detected (and
// in doing so replace
// any existing AnonymousAuthenticationToken). See SEC-610.
if (existingAuth instanceof AnonymousAuthenticationToken) {
return true;
}
return false;
}
As you can see getAuthhentication invoked on SecurityContextHolder return object set in previous request.
Sorry for my bad English.
UPDATE: you can invalidate session using "/logout" url.

Spring Security logout does not work - does not clear security context and authenticated user still exists

I know, there are many articles about this topic, but I have a problem and I can't find any solution.
I have a classic spring security java config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuctionAuthenticationProvider auctionAuthenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(auctionAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequest = http.authorizeRequests();
configureAdminPanelAccess(authorizeRequest);
configureFrontApplicationAccess(authorizeRequest);
configureCommonAccess(authorizeRequest);
http.csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
http.logout()
.clearAuthentication(true)
.invalidateHttpSession(true);
}
...
}
Also, I have two controller methods, where I login/logout from my web application by AJAX.
When I would like to logout, I first call this method, which I expect to clear user sessions and clear everything from the security context.
#Override
#RequestMapping(value = "/logout", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Boolean> logout(final HttpServletRequest request, final HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return new ResponseEntity<>(Boolean.TRUE, HttpStatus.OK);
}
After this I reload my client web application and each time, when it is reloaded, I check whether the user is authenticated by calling the following controller method:
#Override
#RequestMapping(value = "/user", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<UserDetails> user() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
return new ResponseEntity<>((UserDetails) principal, HttpStatus.OK);
}
return null;
}
And here I aways receive the last authenticated user. It seems that in the previous logout method, Spring logout doesn't work.
Keep in mind that I tried to logout with the following code, without any success:
#Override
#RequestMapping(value = "/logout", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Boolean> logout(final HttpServletRequest request) {
try {
request.logout();
return new ResponseEntity<>(Boolean.TRUE, HttpStatus.OK);
} catch (ServletException ex) {
if (LOG.isDebugEnabled()) {
LOG.debug("There is a problem with the logout of the user", ex);
}
}
Are you have any idea what I miss in my config and the logout process?
From your question, I see you are trying to create your own logout and you also trying to use the default Spring logout. I advise that you should choose one method and not mix them both. There are two I recommend to logout from Spring:
First: Default spring security logout
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/logout.done").deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
From the example above, you should only need to call the /logout URL whenever you want to logout the user. No need to create any #Controller to handle that logout instead Spring will help to log the user out. You also can add other thing you want to invalidate here.
Second: Programmatically logout
#RequestMapping(value = {"/logout"}, method = RequestMethod.POST)
public String logoutDo(HttpServletRequest request,HttpServletResponse response){
HttpSession session= request.getSession(false);
SecurityContextHolder.clearContext();
session= request.getSession(false);
if(session != null) {
session.invalidate();
}
for(Cookie cookie : request.getCookies()) {
cookie.setMaxAge(0);
}
return "logout";
}
If you are using this logout approach, you don't need to include the first method in ht eSpring security config. By using this method, you can add an extra action to perform before and after logout done. BTW, to use this logout, just call the /logout url and the user will be logged out manually. This method will invalidate the session, clear Spring security context and cookies.
In addition for the second method, if you are using RequestMethod.POST, you need to include the CSRF key on the POST request. The alternative way is to create a form with a hidden input CSRF key. This is some example of auto generated logout link with jQuery :
$("#Logout").click(function(){
$form=$("<form>").attr({"action":"${pageContext.request.contextPath}"+"/logout","method":"post"})
.append($("<input>").attr({"type":"hidden","name":"${_csrf.parameterName}","value":"${_csrf.token}"}))
$("#Logout").append($form);
$form.submit();
});
You just need to create a hyperlink <a id="Logout">Logout</a> to use it.
If you are using RequestMethod.GET,just include a CSRF key as a parameter in you link like this:
Logout
Thats all, hope it helps.
Just a heads up, there is Clear Site Data HTTP header as shown below
Clear-Site-Data: "cache", "cookies", "storage", "executionContexts"
I also helped add support for Clear-Site-Data header into Spring-Security 5.2 project. For more details around the implementation, see the PR.
Here is a sample of how it is going to work
#EnableWebSecurity
static class HttpLogoutConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.logout()
.addLogoutHandler(new HeaderWriterLogoutHandler(
new ClearSiteDataHeaderWriter(SOURCE)));
}
}
Where SOURCE is a vararg of one or more of the following
"*" Clear everything
One or more of "cache", "cookies", "storage", "executionContexts"
For more details see the sample test in the LogoutConfigurerClearSiteDataTests.java.
This will help, i think clearAuthentication(true) is enough:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
....
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.httpBasic()
.and()
.logout().clearAuthentication(true)
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.and()
I solved my problem similarly by adding the following parameter to the application.properties file
spring.cache.type=NONE
Just change logout URL from "/logout" to "war or snapshot name/logout"

Categories