Spring Security Guide for Login

I use spring security to implement software, manual user input. I have a scenario in which I positively set the user ID and want to register it. I don’t know their password and therefore I can’t use the usual login code path in which you submit the form to the URL that spring intercepts through the Filter servlet, doing all this with auth + session magic.

I searched and it seems that most people create their own Authentication object and then tell spring about: via

 PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(user, "", user.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); 

Indeed it works. spring even puts it in a session for me, making subsequent HTTP requests, keeping their auth status.

However, I feel like this is a dirty hack. I will provide some details that I hope will give specific examples of problems associated with using setAuthentication() inside the controller to achieve manual login:

To give an idea, my configuration is:

 httpSecurity .authorizeRequests() .antMatchers("/test/**").permitAll() .antMatchers("/admin/**", "/api/admin/**").hasRole("USER_SUPER_ADMIN") .and() .formLogin() .loginPage("/sign-in?sp") .loginProcessingUrl("/api/auth/sign-in") .successHandler(createLoginSuccessHandler()) .failureHandler(createLoginFailureHandler()) .permitAll() .and() .logout() .logoutUrl("/api/auth/sign-out") .logoutSuccessHandler(createLogoutSuccessHandler()) .and() .sessionManagement() .maximumSessions(1) .maxSessionsPreventsLogin(true) .sessionRegistry(sessionRegistry) ; 

Key points above:
- I use custom successful and fault-tolerant functions to enter the form - I want to configure the behavior for maximum simultaneous sessions per user

- I want to support spring default session commit protection (changing the session ID at login).
- I want to use the session registry
-... more if I decided to configure it.

I went through the code to see how spring handles form registration. As expected, spring does everything my HttpSecurity configurator HttpSecurity when I use the form login. But when I do my own custom / manual login via SecurityContextHolder.getContext().setAuthentication() , it does nothing. This is because spring does all this with the auth + session file inside the Filter servlet, and my program code cannot really call the filter. Now I can try to add the missing behavior myself, duplicating their code: I see that the filter uses: ConcurrentSessionControlAuthenticationStrategy , ChangeSessionIdAuthenticationStrategy and RegisterSessionAuthenticationStrategy . I can create these objects myself, configure them and call them after my user login. But, something like this to duplicate it that way. In addition, there are still other ways that I am missing - I noticed that when using the form login code path, spring fires some login events that do not fire when I make my user login. And maybe there are other things that I miss or don't understand. The whole process is quite complicated.

So, I feel like approaching this wrong. Should I use a different strategy so that I don't get around so many things spring does for me? Perhaps I should try to make my own AuthenticationProvider to complete this setup login?

* To clarify, my code works more or less. But I feel like I did it using a bad strategy because I had to write code duplicating a lot of the things spring does for me. Also, my code does not perfectly reproduce what spring does, making me wonder what negative consequences might arise. There must be a better way to programmatically reach the entrance.

-one
java spring spring-security
Nov 11 '17 at 0:44
source share
2 answers

For user authentication on the Internet, you must implement a combination of a custom authentication filter (e.g. AbstractAuthenticationProcessingFilter or just GenericFilterBean ), a user authentication provider ( AuthenticationProvider ), and / or a user authentication token ( AbstractAuthenticationToken ).

For example, see Spring Kerberos security source.

See also:

+1
Nov 14 '17 at 12:10
source share

I wanted to talk about how I implemented the dur tip. In my scenario, I used only custom AuthenticationProvider . Instead of creating a custom Filter servlet, such as the AbstractAuthenticationProcessingFilter extension, which seemed like a lot of work, I decided to use the following strategy instead:

  • In my code, where I was sure that I had identified the user and wanted them to be "logged in", I set a flag in the user session, noting that they should be logged in the next request, as well as any other information about the person / account I needed, such as their username.
  • Then I told the browser client to make an http message in loginProcessingUrl (the same one I configured for spring security to enter the form), telling them to send the standard username and password form params, although they do not need to send real values ​​- dummy values, such as foo , beautiful.
  • When the user makes this post request (e.g. before /login ), spring will output my custom AuthenticationProvider , which will look like in the user session to check the flag and collect the username. It will then create and return an Authentication object, such as a PreAuthenticatedAuthenticationToken , which identifies the user.
  • Spring will handle the rest. The user is now logged in.

This way you stay in the β€œnormal” way of logging in, and therefore spring will be automatically:

  • Call any user-defined successful and fault-tolerant handlers that you configured to enter the form, which is nice if you use this place to perform certain actions at login, for example, to request or update db.
  • It will comply with any maximum concurrent sessions on user settings that you can use.
  • You can keep spring protection against session attacks by default (changing session ID at login).
  • If you set a custom session timeout, for example, through server.session.timeout in the properties file, spring will use it. There are probably other session configuration attributes that are running at this time too.
  • If you enable the spring function "remember me", it will work.
  • It will fire a login event, which is used for other spring components, such as saving a user session to SessionRegistry . I think events are also used by other parts of spring, such as the actuator and for auditing.

When I first tried just to make the usually recommended SecurityContextHolder.getContext().setAuthentication(authentication) for logging in to my user, instead of the user AuthenticationProvider , none of the above bullets were executed for me, which could completely violate your application ... or cause subtle security errors - nothing is good.

Here is some code to help reinforce what I said:

Custom AuthenticationProvider

 @Component public class AccountVerificationAuthenticationProvider implements AuthenticationProvider { @Autowired private AppAuthenticatedUserService appAuthenticatedUserService; @Autowired private AuthService authService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // This will look in the user session to get their username, and to make sure the flag is set to allow login without password on this request. UserAccount userAccount = authService.getUserAccountFromRecentAccountVerificationProcess(); if (userAccount == null) { // Tell spring we can't process this AuthenticationProvider obj. // Spring will continue, and try another AuthenticationProvider, if it can. return null; } // A service to create a custom UserDetails object for this user. UserDetails appAuthenticatedUser = appAuthenticatedUserService.create(userAccount.getEmail(), "", true); PreAuthenticatedAuthenticationToken authenticationToken = new PreAuthenticatedAuthenticationToken(appAuthenticatedUser, "", appAuthenticatedUser.getAuthorities()); authenticationToken.setAuthenticated(true); return authenticationToken; } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } } 

Spring security configuration for using provider

 // In your WebSecurityConfigurerAdapter @Configuration @EnableWebSecurity public class AppLoginConfig extends WebSecurityConfigurerAdapter { @Autowired private AccountVerificationAuthenticationProvider accountVerificationAuthenticationProvider; @Override protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { // Spring will try these auth providers in the order we register them. // We do the accountVerificationAuthenticationProvider provider first, since it doesn't need to do any IO to check, // so it very fast. Only if that one rejects, do we then try the active directory one. authenticationManagerBuilder .authenticationProvider(accountVerificationAuthenticationProvider) // I'm using ActiveDirectory, but whatever you want to use here should work. .authenticationProvider(activeDirectoryLdapAuthenticationProvider); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { ... } } 
0
Nov 28 '17 at 3:43 on
source share



All Articles