Spring security makes a session when a user access the log in page - spring

I'm making log in page, following official guide.
I want to add a function which makes redirect to home if a user join.
The logic is like this.
A user join with email, password and username.
Joinpage redirect to log in page.
if there is authentication, directly go to home.
How can I redirect to home with authentication?
#Slf4j
#Controller
#RequiredArgsConstructor
public class LoginController {
#GetMapping("/login")
public String loginForm(#ModelAttribute LoginForm loginForm) {
return "login/loginForm";
}
}
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserSecurityService userSecurityService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() // 인가 요청 받기
.antMatchers("/", "/home", "/join",
"/css/**", "/*.ico", "/error").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.failureUrl("/login")
.usernameParameter("email")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.invalidateHttpSession(true);
}

basically, the security generates user session along the SessionCreationPolicy value when the user logins successfully.
if you want to change the session system to stateless system.
but following the below code.
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
and then, you need to set the authentication object.
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("principal", "credentials", null));

Related

How to make a custom UsernamePasswordAuthenticationFilter register at an endpoint other than /login?

I've been following a tutorial to implementing JWT authentication in Spring Boot but am trying to adapt it to a case where I have two WebSecurityConfigurerAdapter classes, one for my API (/api/** endpoints) and one for my web front-end (all other endpoints). In the tutorial, a JWTAuthenticationFilter is created as a subclass of UsernamePasswordAuthenticationFilter and added to the chain. According to the author, this filter will automatically register itself with the "/login" endpoint, but I want it to point somewhere different, such as "/api/login" because I'm using this authentication method for my API only.
Here's the security configuration code for both the API and front-end (with some abbrevation):
#EnableWebSecurity
public class MultipleSecurityConfigurations {
#Configuration
#Order(1)
public static class APISecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
}
#Configuration
public static class FrontEndSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/?logout")
.and()
.authorizeRequests()
.mvcMatchers("/").permitAll()
.mvcMatchers("/home").authenticated()
.anyRequest().denyAll()
;
}
}
}
The question is: how can I define an endpoint such as "/api/login" as the endpoint for my custom JWTAuthenticationFilter?
Or, do I need to change the filter to not be a subclass of UsernamePasswordAuthenticationFilter and if so, how would I configure that?
EDIT: Something I've tried:
I guessed that the /api/login endpoint needed to be .permitAll() and I tried using formLogin().loginProcessingUrl(), even though it's not really a form login - it's a JSON login. This doesn't work. When i POST to /api/login I end up getting redirected to the HTML login form as if I were not logged in. Moreover, my Spring boot app throws a weird exception:
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
The configuration I'm trying now:
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.formLogin().loginProcessingUrl("/api/login").and()
.authorizeRequests()
.antMatchers("/api/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
Since JWTAuthenticationFilter is a UsernamePasswordAuthenticationFilter, you could change the login endpoint directly on the filter instance:
JWTAuthenticationFilter customFilter = new JWTAuthenticationFilter(authenticationManager());
customFilter.setFilterProcessesUrl("/api/login");
http.addFilter(customFilter);
This configures JWTAuthenticationFilter to attempt to authenticate POST requests to /api/login.
If you wish also to change the default POST to another method (e.g. GET), you can set the RequiresAuthenticationRequestMatcher instead. For instance:
customFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/login", "GET"));

Providing custom OAuth2AccessTokenResponseClient to spring

I have a demo spring boot app and I would like to configure oauth login as well my own custom token response client.
This is my configuration:
#Configuration
#EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.and()
.oauth2Client()
.authorizationCodeGrant()
.accessTokenResponseClient(customAccessTokenResponseClient());
}
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> customAccessTokenResponseClient() {
DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient();
client.setRequestEntityConverter(new CustomOAuth2AuthorizationCodeGrantRequestEntityConverter());
return client;
}
}
The login flow is working find, but it looks like my custom token response client is not getting registered. I put a break point in there, but the flow doesn't go through it. It looks like it is still using the defaults.
Am I missing something here or is the order wrong?
Figured out the issue.
The right way to configure is the following:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated()
.and()
.oauth2Login()
.tokenEndpoint()
.accessTokenResponseClient(customAccessTokenResponseClient());
}

Login page needs to be prompted if user is not authorized to access specific controller or URL in spring security. How to achieve that?

I'm using spring-boot, spring-security and JSP. If I click on a button it should go to a controller if user is logged in. Otherwise, it should first ask user to login and then get back to that page. In short, user should see the page if he is logged in. How can I achieve this?
I think filters/antmatchers might be used but I am wondering how the user will get back to that particular page/controller after logging in?
Try using something like this to allow users access to certain pages and then set the default success url accordingly. You can have a home page as I use here represented by "/" and once a user logs in they are redirected to your /welcome page.
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Public access to login, landing, and error pages
http.authorizeRequests().antMatchers("/", "/login", "/errorpage").permitAll();
// Static resource permissions
http.authorizeRequests()
.antMatchers("/css/**", "/fonts/**", "/images/**", "/webfonts/**", "/js/**", "/webjars/**", "/messages/**")
.permitAll();
// Login specifications
http.formLogin().loginPage("/login").defaultSuccessUrl("/welcome", true);
// Logout specifications
http
.logout()
.deleteCookies("remove")
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.permitAll();
}
}
Inside WebSecurityConfigurerAdapter implementation, you need to inform a formLogin and specify the loginPage.
That's just enough to Spring to use the endpoint /login this way.
If you try to access a page without logged, for example /profile, you will be redirected to /login, and after logged, you'll be redirected to /profile
And in this example, you have 3 pages accessible without authentication / ,/homeand/info`
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home", "/info" ).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
...
}

Spring Security: Multiple http elements with Multiple AuthenticationManagers

I am struggling with Java Config for Spring Security. I have multiple entry points but I cannot get the AuthenticationManagers provisioned correctly.
My first configuration file is like this:
#Configuration
#EnableWebSecurity
#Order(100)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.antMatcher("/service/**")
.addFilterAfter(requestHeaderAuthenticationFilter(), SecurityContextPersistenceFilter.class)
.authorizeRequests()
.antMatchers("/service/**").authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.authenticationProvider(preAuthenticatedAuthenticationProvider(null));
}
#Bean
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter() throws Exception
{
// Takes the value of the specified header as the user principal
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setPrincipalRequestHeader("SECRET_HEADER");
filter.setAuthenticationManager(authenticationManager());
filter.setExceptionIfHeaderMissing(false);
return filter;
}
This all works correctly. When I set a breakpoint in the RequestHeaderAuthenticationFilter I see an AuthenticationManager with one AuthenticationProvider, and that is the preAuthenticatedAuthenticationProvider (not shown because is just a regular old bean).
I also have a special security chain for admin users and the like:
#Configuration
#Order(101)
public class AdminSecurity extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authenticationProvider(mainSiteLoginAuthenticationProvider())
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/admin/**").access("SECRET ADMIN ACCESS EXPRESSION")
.antMatchers("/internal/**").access("SECRET INTERNAL ACCESS EXPRESSION")
.anyRequest().permitAll()
.and()
.formLogin()
.defaultSuccessUrl("/admin/thing")
.loginPage("/login")
.loginProcessingUrl("/do_login")
.defaultSuccessUrl("/admin/thing")
.failureUrl("/login?error=true")
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.and()
.exceptionHandling()
//.authenticationEntryPoint(null) // entry-point-ref="loginEntryPoint"
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // create-session="ifRequired"
.and()
.csrf().disable();
}
This is now working (after a lot of struggle), but if I put a breakpoint in the UsernamePasswordAuthenticationFilter, I see that this filter has a different AuthenticationManager instance, which is provisioned with the mainSiteLoginAuthenticationProvider as expected. However, it has a parent AuthenticationManager which is provisioned with the default DaoAuthenticationProvider that generates a temporary password in the logs:
Using default security password: 47032daf-813e-4da1-a224-b6014a705805
So my questions are:
How can I get both security configs to use the same AuthenticationManager? I thought that the SecurityConfig, being order 100, would create one, and then AdminConfig, being 101, would just use it. But I have been unable to get them to use the same AuthenticationManager.
Failing that, how can I prevent the AuthenticationManger of AdminConfig from generating a parent that has the default DaoAuthenticationProvider?
I am using Spring Boot 1.5.9.RELEASE, which means Spring Security 4.2.3.RELEASE.

Spring Boot redirect to current page after successful login

I have login forms in modal windows. After successful login user is redirected to / page. I am trying to find a method to stay on contact page or another page after login. How to do this? My code is:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**","/js/**","/fonts/**","/images/**","/home","/","/kontakt").permitAll()
.antMatchers("/userlist").hasRole("ADMIN")
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/");
}
You could use custom AuthenticationSuccessHandler and set useReferer to true.
#Bean
public AuthenticationSuccessHandler successHandler() {
SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
handler.setUseReferer(true);
return handler;
}
And in your configure method:
http
.formLogin()
.loginPage("/login")
.successHandler(successHandler())
.permitAll()
.and()
Just to provide an alternative solution:
formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
defaultSuccessUrl is a shortcut to adding the custom SuccessHandler.
I had a weird issue that would cause on login to redirect the user to localhost:8080/js/bootstrap.min.js
If anyone else is experiencing an odd redirection on login, which seems to override the .defaultSuccessUrl(), then try adding this code below in SecurityConfig:
#Override
public void configure(WebSecurity security){
security.ignoring().antMatchers("/css/**","/images/**","/js/**");
}
Add all your Resources/static folders to the antMatchers()
You can as well do it in your AuthenticationSuccessHandler implementation:
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws
IOException, ServletException
{
//Do your logic;
response.sendRedirect(request.getHeader("referer");
}
Config is same as the accepted answer,
only luck I had was with extending SavedRequestAwareAuthenticationSuccessHandler.
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
System.out.println("HEP HEP");
super.onAuthenticationSuccess(request, response, authentication);
}
}
I had absolutely the same issue with inadequate redirect after adding bootstrap to my project tree.
Method .defaultSuccessUrl with flag = true saved me time and lines of code.
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/", true)
.permitAll()
The Spring route, ala extending SavedRequestAwareAuthenticationSuccessHandler or SimpleUrlAuthenticationSuccessHandler can be a bit clunky to implement. In the controller (ex. one that processes logins), you can do the header request yourself; ex:
HttpServletRequest request =null;
String priorUrl = request.getHeader("Referer");
You will notice that you will have the URL prior to either a manual (initiated by user) logout or a session timeout (as handled by Spring session): you'll get an https://iAmPriorUrl.com/.... Then you can do whatever you want with it.
#Jonas All you need to do is add .requestCache() at the end
you config will look like this
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.requestCache()

Resources