Spring security Login does NOT propagate my form(POST) to Controller - spring-mvc

On Home page (permitAll() )
I have a form (Action "/saveX" authenicated() )
I have a Controller with #PostMapping(value="/saveX")
User fills in form data & hits submit (POST)
Spring redirects to default login page
User enters username & password & hits login
User is now presented with Home page (logged-in features are now visible)
Was expecting Spring (successful)login to forward to my (#PostMapping)Controller
Now that User is logged-In
User fills in form data & hits submit (POST)
Controller with #PostMapping(value="/saveX") is invoked
Question
Shouldn't successful login result in continuing with the original action (ie my form POST)
NOTE - there will be many paths which could be the 1st reason for Authenticating the User --- so I won't have a default successfullogin link that can be used
I want to get these basics sorted out before I move on to implementing more sophisticated Authentication mechanisms - so will be replacing Spring default login later
Any help/hints greatly appreciated

You can do this by implementing AuthenticationSuccessHandler and need to extend SimpleUrlAuthenticationSuccessHandler which will give you options on redirection decisions.
public class RefererRedirectionAuthenticationSuccessHandler
extends SimpleUrlAuthenticationSuccessHandler
implements AuthenticationSuccessHandler {
public RefererRedirectionAuthenticationSuccessHandler() {
super();
setUseReferer(true);
}
}
In Spring Security configuration file, you need to register your AuthenticationSuccessHandler using successHandler() method.
#Configuration
#EnableWebSecurity
public class BasicConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
//put your logic here
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login*")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.successHandler(new RefererAuthenticationSuccessHandler());
}
}

Related

spring-boot security identify by token

I got an app and I wanna create a connection to my rest-api.
Each user will get a "token" which will automatically be refreshed by google and co. In my requests, I will send the token and if it can be resolved to the user, the request should be answered, else if it is not up to date, I just wanna drop the request and return an error.
Are there still some possibilities?
Thanks for your help!
Current starting:
https://gist.github.com/PascalKu/97bca9506ad4f31c9e13f8fe8973d75b
You need to implement custom authentication in spring. I did the same thing but I had a db like:
fb_email_address | user_id | other_fields...
You must create these classes:
#Component
class TokenAuthenticationFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain) {
String theToken = request.getParameter('theToken');
TokenAuthentication tokenAuth = new TokenAuthentication(theToken)
SecurityContextHolder.getContext().setAuthentication(tokenAuth)
}
}
You need to add the authentication provider to spring's security system:
#Configuration
#EnableWebSecurity
class WebConfigHolder extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
#Autowired private TokenAuthenticationProvider tokenAuthenticationProvider
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider)
}
}
Implement authentication provider which actually checks to see if the token is valid.
#Component
class TokenAuthenticationProvider implements AuthenticationProvider {
//called by provider manager at some point during execution of security filters, I think
//it's the security api's job to call this
//the fbauthentication we create in our fbauthenticationfilter gets passed into this
#Override
#Transactional
Authentication authenticate(Authentication auth) {
TokenAuthentication tokenAuthentication = (TokenAuthentication) auth;
String theToken = auth.getThetoken();
boolean theTokenIsInDB = ///CHECK TO SEE IF TOKEN IS IN DB
if(theTokenIsInDB) {
TokenAuthentication t = new TokenAuthentication();
t.setAuthenticated(true);
return t;
} else {
throw new BadCredentialsException("Could not find user");
}
}
#Override
boolean supports(Class<?> authentication) {
boolean ret = TokenAuthentication.isAssignableFrom(authentication)
return TokenAuthentication.isAssignableFrom(authentication)
}
}
You need a simple Authentication Class that is just the object that's used to store the credentials while spring is waiting for the thread to get to the spring security filter; once it gets to that filter it passes authentication objects to the providers that support them. This allows you to have multiple authentication methods like FB, Google, custom tokens, etc... In my app I use FB tokens and in my provider, I check to see if the FB token corresponds to an authorized email address on my whitelist of email addresses. If it does, the user gets access to my app.
public class TokenAuthentication extends Authentication{
String token;
boolean isAuthenticated = false;
public TokenAuthentication(String theToken) { this.token = theToken;}
//getters and setters
}
What this code all does is, whenever someone accesses your API such as /api/person/get?theToken=132x8591dkkad8FjajamM9
The filter you created is run on every request. It checks to see if theToken was passed in and adds the TokenAuthentication to spring security.
At some point in the filter chain, spring security filter will run, and it will see that a TokenAuthentication has been created, and will search for a provider that can perform authentication on that. That happens to be your TokenAuthenticationProvider.
TokenAuthenticationProvider does the actual authentication. If it returns an authentication object that has isAuthenticated set to true, then the user will be allowed to access that api call.
Once authenticated, a user doesn't need to pass theToken again until his cookies are cleared or you invalidate his session. So he can call /api/person without the query parameters for the rest of his interactions. That's because the authentication is stored as a session-scoped data in spring.
Hope that helps. Let me know if anything's missing.

hasAuthority method for only POST and PUT httpmethods

Here is my configure code snippet
#Override
public void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/user/**").hasAnyAuthority("ROLE_ADMIN")
.antMatchers("/api/status/**").hasAuthority("ROLE_ADMIN").anyRequest()
.authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
In this I need to make ROLE_ADMIN to access only POST and PUT httpmethods. He should not be able to access GET or DELETE httpmethod. I need this to be done in a single .antMatchers() method.
How can I do this?
Have a look at this Spring example project. You can define matchers per path and HTTP verb.
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/employees").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/employees/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PATCH, "/employees/**").hasRole("ADMIN")

why spring security makes spring mvc 's postmapping controllder do not work

when I config spring security like this
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Bean
public UserDetailsService userDetailsService(){
return new MyUserDetailsService();
}
#Bean
public MyAuthenticationProvider myAuthenticationProvider(){
MyAuthenticationProvider provider = new MyAuthenticationProvider();
provider.setUserDetailsService(userDetailsService());
return provider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
}
and then I config my controller like this
#GetMapping("/login")
public String showLoginPage(){
System.out.println("GetMapping");
return "login";
}
#PostMapping("/login")
public void authUser(#RequestParam String username,#RequestParam String password){
// just for testing
System.out.println("PostMapping");
}
and then I visit my login page and enter my username and password, but the console doesn't print "PostMapping", which means the program doesn't go into my method "authUser" with #PostMapping.
Though my program runs successfully, but it makes me quite confuse.I suppose spring security doing some work automatically, but now I have no idea where to add my Authentications to the SecurityContextHolder.
I hope somebody can help and thanks very much
It has done by UsernamePasswordAuthenticationFilter, and the default processing path is Post /login, and the Authentication already exist in SecurityContextHolder, you can get it in controller.
If you want to disable form login, change to this.
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated().and()
.formLogin().disable();
Normally, POST mappings are filtered by CSRFfilters. Although it is not recommended in the production environment, you can disable CSRF filter simply using for learning cases:
http.authorizeRequests().anyRequest().authenticated().and().httpBasic()
.and().logout()
.and().csrf().disable();

How to prevent sensitive fields updated by client

For example, in an web appliation, I have a user model:
class User{
String username;
String email;
String passowrd;
boolean active;
Set<Role> roles;
}
The following operations are supported:
1 guest can register(create a new user)
2 user can upate its info
3 user with role of admin can set the `active` and `roles`
At the server side we use the SpringMVC to get the model User directly:
#RequestMapping(value = "", method = RequestMethod.POST)
protected Result create(#Valid #RequestBody User user, BindingResult bindingResult) {
.....
}
So far so good with normal workflow, but think about someone(not admin user) send that:
/user HTTP/Update
{
"username":"jk",
"active":true,
"roles":[{
id:"role_admin_id"
}]
}
If this requset is accepted, the user jk will have the role of super_admin, which is not expected.
How do you protect that?
First of all, #RequestBody User user you sent is just a regular object you wanna update. It's not Spring Security User. If you want to define User as in spring security user, you' ll have to implements UserDetails. Do you already have spring security setup correctly? I don't know if you use xml or java configuration. If you use java configuration, you can control the access by roles as follow:
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
// ...
.formLogin();
}
Reference: http://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#authorize-requests

Spring MVC 4 Global Basic HTTP Authentication

I need to set up global basic HTTP authentication for a staging server. Nothing fancy. I just want to require username/password to access anything. I also would like to use only Java config. I've experimented with a lot of different solutions, but none of them working. I'm always able to access all resources on the server. This is what I'm doing now:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("Configuring HttpSecurity");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("Configuring global AuthenticationManagerBuilder");
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
I can see in the logs that these snippets are being executed. As you can see, in the first method, I am requiring that all requests are authenticated. In the second method, I am specifying in memory authentication.
Your SOP statements are getting printed (while container instantiation) because of #Configuration (which is again not required as it is also declared by #EnableWebSecurity). You still need to register the spring security filter chain in your web.xml or MVC initializer class that extends WebMvcConfigurerAdapter or implements WebApplicationInitializer if you wish to use it with the application filter chain. For example (java config as you are looking for the same):
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(
DispatcherType.REQUEST, DispatcherType.ERROR);
container.addFilter("springSecurityFilterChain",
DelegatingFilterProxy.class).addMappingForUrlPatterns(
dispatcherTypes, false, "/*");
where container is an instance of ServletContext.

Resources