how could I use csrf in spring security

2020-06-30 05:26发布

问题:

My login page.

<form class="form-horizontal" ng-controller="loginCtrl" action="/login" method="post">
    <div class="form-group input-login">
        <div ng-if="message.error" class="alert alert-danger">
            <p>Invalid username and password.</p>
        </div>
        <div ng-if="message.logout" class="alert alert-success">
            <p>You have been logged out successfully.</p>
        </div>
        <label  class="control-label sr-only">Email</label>
        <div class="col-md-12">
            <input type="text" class="form-control" ng-model="user.username" name="username" placeholder="NickName"/>
        </div>
    </div>
    <div class="form-group input-login">
        <label  class="control-label sr-only">Password</label>
        <div class="col-md-12">
            <input type="password" class="form-control" ng-model="user.password" name="password" placeholder="Password"/>
        </div>
    </div>
    <input name="_csrf" type="hidden" value="6829b1ae-0a14-4920-aac4-5abbd7eeb9ee" /> 
    <div class="form-group sub-login">
        <div class=" col-md-12">
            <button name="submit" type="submit" class="btn btn-primary btn-login">Login</button>
        </div>
    </div>
</form>

But if I didn't disable the csrf,it alway be accessDenied.I don't know where is the problem.

My config code below.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDao userDao;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(new UserService(userDao)).passwordEncoder(new MD5Util());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .antMatchers("/", "/index").access("hasRole('USER')")
                .and()
                    .formLogin()
                    .loginPage("/login")
                    .failureUrl("/login#/signin?error=1")
                    .successHandler(new LoginSuccessHandler())
                    .usernameParameter("username").passwordParameter("password")
                .and()
                    .logout()
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/login#/signin?logout=1")
                .and()
                    .exceptionHandling().accessDeniedPage("/Access_Denied")
                .and().csrf().disable(); // If I  disable this csrf,it worked!

    }
}

And does anyone knows how to ues thymeleaf in ng-route's partial page.Just see this question.

回答1:

Your best bet would be to have a look at this link: https://spring.io/blog/2015/01/12/the-login-page-angular-js-and-spring-security-part-ii

Particularly, the relevant section is:

CSRF Protection

That’s good because it means that Spring Security’s built-in CSRF protection has kicked in to prevent us from shooting ourselves in the foot. All it wants is a token sent to it in a header called “X-CSRF”. The value of the CSRF token was available server side in the HttpRequest attributes from the initial request that loaded the home page. To get it to the client we could render it using a dynamic HTML page on the server, or expose it via a custom endpoint, or else we could send it as a cookie. The last choice is the best because Angular has built in support for CSRF (which it calls “XSRF”) based on cookies.

So all we need on the server is a custom filter that will send the cookie. Angular wants the cookie name to be “XSRF-TOKEN” and Spring Security provides it as a request attribute, so we just need to transfer the value from a request attribute to a cookie:

public class CsrfHeaderFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
                .getName());
        if (csrf != null) {
            Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
            String token = csrf.getToken();
            if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
                cookie = new Cookie("XSRF-TOKEN", token);
                cookie.setPath("/");
                response.addCookie(cookie);
            }
        }
        filterChain.doFilter(request, response);
    }
}

After a bit more work, the last sentence is:

With those changes in place we don’t need to do anything on the client side and the login form is now working.



回答2:

You should include an input hidden to send the CSRF token in the POST method when the user submit the form.

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

You already included a input hidden _csrf in your template, but the value is wrong, just change it.

You can read more about CSRF here:

https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html