如何将包含 CSRF 令牌的数据从 angularjs 控制器提交到 REST 控制器?

Posted

技术标签:

【中文标题】如何将包含 CSRF 令牌的数据从 angularjs 控制器提交到 REST 控制器?【英文标题】:How to submit data including the CSRF token from angularjs controller to REST controller? 【发布时间】:2018-11-04 09:46:37 【问题描述】:

我正在尝试使用从角度控制器到 REST 控制器的模式提交数据。 PUT,GET,POST 方法工作代码仅在 CSRF 被disbaled 时起作用,但当我 启用 CSRF 时 PUT 和 POST 不起作用。我检查了控制台日志,上面写着 405 (Method Not Allowed)

我知道我需要将数据与 csrf 令牌 一起传递,但我不知道在使用角度控制器传递数据时如何包含它。这是我的第一个角度。任何建议都非常感谢。所以这是我的代码。

我的配置:

public class CORSFilter implements Filter 

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException 
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type");
        chain.doFilter(req, res);
    

    public void init(FilterConfig filterConfig) 

    public void destroy() 


我的 SecurityConfiguration.java

Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Autowired
    @Qualifier("customUserDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    PersistentTokenRepository tokenRepository;

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth)
            throws Exception 
        auth.userDetailsService(userDetailsService);
        auth.authenticationProvider(authenticationProvider());
    

    /* This method sets-up the list of accessing page for each role. */
    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.authorizeRequests()
                // Request Mapping accessible
                .antMatchers("/restUser/**") // -------> HERE I INCLUDE THE REQUEST MAPPING
                // Roles
                .access("hasRole('ADMIN')").and().formLogin()
                .loginPage("/login").loginProcessingUrl("/login")
                .usernameParameter("usernameId").passwordParameter("password").and()
                .rememberMe().rememberMeParameter("remember-me")
                .tokenRepository(tokenRepository).tokenValiditySeconds(86400)
                .and().csrf().and().exceptionHandling()
                .accessDeniedPage("/Access_Denied");

        // Disable spring security
        // http.csrf().disable(); 

    

    @Bean
    public PasswordEncoder passwordEncoder() 
        return new BCryptPasswordEncoder();
    

    @Bean
    public DaoAuthenticationProvider authenticationProvider() 
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    

    @Bean
    public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() 
        PersistentTokenBasedRememberMeServices tokenBasedservice = new PersistentTokenBasedRememberMeServices(
                "remember-me", userDetailsService, tokenRepository);
        return tokenBasedservice;
    

    @Bean
    public AuthenticationTrustResolver getAuthenticationTrustResolver() 
        return new AuthenticationTrustResolverImpl();
    


我的 RestController:

@RestController
public class UserRestController 

    @Autowired
    UserService userService;

    // -------------------Retrieve All
    // Users--------------------------------------------------------

    @RequestMapping(value = "/restUser", method = RequestMethod.GET)
    public ResponseEntity<List<User>> listAllUsers() 
        List<User> users = userService.findAllUsers();
        if (users.isEmpty()) 
            return new ResponseEntity<List<User>>(HttpStatus.NO_CONTENT);
        
        return new ResponseEntity<List<User>>(users, HttpStatus.OK);

    

    // -------------------Create a
    // User--------------------------------------------------------

    @RequestMapping(value = "/restUser", method = RequestMethod.POST)
    public ResponseEntity<Void> createUser(@RequestBody User user,
            UriComponentsBuilder ucBuilder) 
        System.out.println("Creating User " + user.getUsernameId());

        // if (userService.isUserExist(user)) 
        // System.out.println("A User with name " + user.getUsername() +
        // " already exist");
        // return new ResponseEntity<Void>(HttpStatus.CONFLICT);
        // 

        userService.saveUser(user);
        System.out.println("Fetch Data: " + user);
        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(ucBuilder.path("/restUser/id")
                .buildAndExpand(user.getId()).toUri());
        return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
    

    // -------------------Retrieve Single
    // User--------------------------------------------------------

    @RequestMapping(value = "/restUser/id", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<User> getUser(@PathVariable("id") int id) 
        System.out.println("Fetching User with id " + id);
        User user = userService.findById(id);
        if (user == null) 
            System.out.println("User with id " + id + " not found");
            return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
        

        System.out.println("Fetch Data: " + user);
        return new ResponseEntity<User>(user, HttpStatus.OK);
    

    // ------------------- Update a User
    // --------------------------------------------------------

    @RequestMapping(value = "/restUser/id", method = RequestMethod.PUT)
    public ResponseEntity<User> updateUser(@PathVariable("id") int id,
            @RequestBody User user) 
        System.out.println("Updating User " + id);

        User currentUser = userService.findById(id);
        //
        // if (currentUser==null) 
        // System.out.println("User with id " + id + " not found");
        // return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
        // 

        // currentUser.setUsernameId(user.getUsernameId());
        // currentUser.setFirstName(user.getFirstName());
        // currentUser.setEmail(user.getEmail());

        System.out.println("USER: " + user);
        userService.updateUser(user);
        return new ResponseEntity<User>(currentUser, HttpStatus.OK);
    


App.js

'user strict';

var App = angular.module('myApp', ['ngResource', 'ngAnimate', 'ui.bootstrap']);

user_service.js

'use strict';


App.factory('User', ['$resource', function ($resource) 
    //$resource() function returns an object of resource class
    return $resource(
            'http://localhost:8080/MavenTry/restUser/:id', 
            id: '@id',//Handy for update & delete. id will be set with id of instance
            
                update: 
                      method: 'PUT' // To send the HTTP Put request when calling this custom update method.
                

            
    );
]);

user_controller.js

'use strict';

App
        .controller(
                'UserController',
                [
                        '$scope',
                        'User',
                        '$uibModal',
                        '$log',
                        function($scope, User, $uibModal, $log) 
                            var self = this;
                            self.user = new User();

                            self.users = [];

                            //Fetch all user data
                            self.fetchAllUsers = function() 
                                self.users = User.query();
                                $log.info('self.users: ', self.users);
                            ;

                            // Call function fetchAllUsers                          
                            self.fetchAllUsers();


                            $scope.open = function(id) 

                                $log.info("parameter id: " + id);
                                $scope.items = [];

                                for (var i = 0; i < self.users.length; i++) 
                                    if (self.users[i].id === id) 
                                        self.user = angular.copy(self.users[i]);
                                        $scope.items = angular
                                                .copy(self.users[i]);
                                        break;

                                    
                                

                                var modalInstance = $uibModal.open(
                                    templateUrl : 'myModal1',
                                    controller : 'ModalInstance',
                                    size : 'lg',
                                    resolve : 
                                        items : function() 
                                            return $scope.items;
                                        
                                    
                                );
                            ;

                                self.reset = function() 
                                self.user = new User();
                                $scope.myForm.$setPristine(); // reset Form
                            ;

                         ]);


// Populate modal with data
App.controller('ModalInstance', function($scope, $uibModalInstance, items,
        $log, User) 

    $scope.form = ;
    var myModal = this;
    myModal.user = new User();

    myModal.users = [];

    myModal.fetchAllUsers = function() 
        myModal.users = User.query();
    ;

    // Populate DataTable using $GET
    myModal.fetchAllUsers = function() 
        myModal.users = User.query();
        ;

    // Create user using $POST
    myModal.createUser = function() 
        myModal.user.$save(function() 
        myModal.fetchAllUsers();
        );
    ;

    // Update $PUT
    myModal.updateUser = function() 
            myModal.user.$update(function() 
            myModal.fetchAllUsers();
        );
    ;


    $scope.userInfo = items;

    $scope.submitForm = function() 
        if ($scope.form.userForm.$valid) 
            console.log('CSRF_TOKEN', csrftoken); // Here i retrieve the token from the view page. Dont know how to include this while submiting data
            myModal.user = $scope.userInfo;

            // Call function for PUT
            myModal.updateUser();

            $uibModalInstance.close('closed');
         else 
            console.log('userform is not in scope');
        

    ;

    $scope.cancel = function() 
        $uibModalInstance.dismiss('cancel');
    ;
);


//Retrieving CSRF_TOKEN
var csrftoken = (function() 
    var metas = window.document.getElementsByTagName('meta');

    // finding one has csrf token
    for (var i = 0; i < metas.length; i++) 
        if (metas[i].name === "csrf-token") 
            return metas[i].content;
        
    

)();

App.constant('CSRF_TOKEN', csrftoken);

在我看来 jsp 我将 csrf 令牌存储在

<meta name="csrf-token" content="$_csrf.token">

我在 Angular 控制器上的代码是超级新手,所以任何建议都非常感谢:)。

【问题讨论】:

真的为什么要投反对票? 【参考方案1】:

你可以通过你喜欢的 jQuery 或纯 javascript 来获得那个 csrf-token。然后您应该将该令牌附加到您的资源标头中。

【讨论】:

感谢劳伦斯的回答,但我更喜欢使用角度控制器而不是 jquery 或纯 javascript。我已经这样做了,现在我正在尝试将代码转换为角度。 好的,请注意任何 DOM 操作都应该在指令中完成,并且尽可能不要在控制器中进行。根据角度最佳实践。 这个链接解决了我的问题。 spring.io/blog/2015/01/12/… .Angular 内置了对基于 cookie 的 CSRF(它称为“XSRF”)的支持。【参考方案2】:

注意:我是 OP,这个答案实际上解决了问题。

解决方案需要在 SecurityConfiguration 类中添加以下行:

/* This method sets-up the list of accessing page for each role. */
    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.authorizeRequests()
                // Request Mapping accessible
                .antMatchers("/restUser/**") // -------> HERE I INCLUDE THE REQUEST MAPPING
                // Roles
                .access("hasRole('ADMIN')").and().formLogin()
                .loginPage("/login").loginProcessingUrl("/login")
                .usernameParameter("usernameId").passwordParameter("password").and()
                .rememberMe().rememberMeParameter("remember-me")
                .tokenRepository(tokenRepository).tokenValiditySeconds(86400)
                .and().csrf().and().exceptionHandling()
                .accessDeniedPage("/Access_Denied").and()
              .addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class).csrf().csrfTokenRepository(csrfTokenRepository());

        

private CsrfTokenRepository csrfTokenRepository() 
          HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
          repository.setHeaderName("X-XSRF-TOKEN");
          return repository;
        

并创建一个 CsrfHeaderFilter.class

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);

    

通过添加.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class).csrf().csrfTokenRepository(csrfTokenRepository()

现在 POST 和 PUT 方法有效。我知道我的代码不是那么干净,需要修改以遵循最佳实践。

参考:https://spring.io/blog/2015/01/12/the-login-page-angular-js-and-spring-security-part-ii

【讨论】:

以上是关于如何将包含 CSRF 令牌的数据从 angularjs 控制器提交到 REST 控制器?的主要内容,如果未能解决你的问题,请参考以下文章

使用 CSRF_COOKIE_HTTPONLY 将 Django CSRF 令牌传递给 Angular

Rails 的 Angular 2 CSRF 令牌

Angular2 和 Django:CSRF 令牌头痛

如何将 django csrf 令牌直接嵌入 HTML?

如何从 Django 在前端获取 CSRF 令牌以及如何在 Postman 中使用它

spring boot angular csrf令牌握手错误