简体   繁体   中英

Angular with Spring Security Login and Authentication

We are using AngularJS in frontend and spring in backend. Spring security shall do the authentication and login, but it doesnt even work with the help of spring's tutorial ( https://spring.io/guides/tutorials/spring-security-and-angular-js/ ). Everytime we are trying to log in the "user"-service the principal object is null. In frontend we are receiving this answer: data = Object {data: "", status: 200, config: Object, statusText: "OK"} EVERYTIME. Doesn't matter logging in with correct or incorrect data...I read so many articles, but I couldn't find a solution.

Our login.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <title>Login</title>
    <link rel="stylesheet" type="text/css" href="stylesheets/bootstrap.min.css" />
    <script src="scripts/angular.min.js"></script>
    <script src="scripts/login.js"></script>
    <style>
        body {
            position: relative;
        }
    </style>
</head>

<body ng-app="LoginApp">
    <div class="modal show" ng-controller="LoginController">
        <div class="modal-header">
            <h1 class="text-center">Login</h1>
        </div>
        <div class="modal-body">
            <form>
                <div class="control-group">
                    <div class="controls">
                        <input class="input-block-level" type="text" placeholder="Username" ng-model="username" ng-change="checkValid()" ng-disabled="requesting">
                    </div>
                </div>
                <div class="control-group">
                    <div class="controls">
                        <input class="input-block-level" type="password" placeholder="Password" ng-model="password" ng-change="checkValid()" ng-disabled="requesting">
                    </div>
                </div>
                <span class="error" ng-bind="errormessage" ng-show="error"></span>
                <!--
                <div class="control-group">
                    <label class="checkbox">
                        <input type="checkbox">Remember me</label>
                </div>
                -->
            </form>
        </div>
        <div class="modal-footer">
            <!--
            <button class="btn btn-link">Forgot password?</button>
            -->
            <button class="btn btnExtra btn-large btn-primary" ng-click="submitLogin()" ng-disabled="requesting || !valid">Login</button>
        </div>
    </div>
</body>

</html>

Our login.js :

(function(angular) {
                const app = angular.module("LoginApp",[]);
                app.controller("LoginController", ["$scope", "$http", function($scope, $http){
                        $scope.username = "";
                        $scope.password = "";
                        $scope.errormessage = "";
                        $scope.error = false;
                        $scope.valid = false;
                        $scope.requesting = false;
                        $scope.submitLogin = function() {
                           $scope.requesting = true;
                           $scope.error = false;
                           const credentials = {
                               username: $scope.username,
                               password: $scope.password
                           };
                           const headers = credentials ? {authorization : "Basic "
                                + btoa(credentials.username + ":" + credentials.password)
                                 } : {};
                           $http.get("user", { headers: headers }).then(function(data){
                               if(data.data.name) {
                                   window.location.href = "/";
                               }
                               else {
                                   $scope.error = true;
                                   $scope.requesting = false;
                                   $scope.errormessage = "Username / Passwort ist falsch!";
                               }
                           },
                           function(reason) {
                               $scope.error = true;
                               $scope.requesting = false;
                               if(reason.status === 404 || reason.status === 408){
                                   $scope.errormessage = "Verbindung zum Server konnte nicht hergestellt werden!";
                               }else if (reason.status === 403){
                                   $scope.errormessage = "Username / Passwort ist falsch!";
                               }else{
                                   $scope.errormessage = "Unbekannter Fehler ist bei der Anfrage aufgetreten! Bitte versuchen Sie es erneut";
                               }
                           })
                        };
                        $scope.checkValid = function(){
                            if($scope.username != undefined && $scope.username != null && $scope.username.length > 1 &&
                            $scope.password != undefined && $scope.password != null && $scope.password.length > 1){
                                $scope.valid = true;
                            }else{
                                $scope.valid = false;
                            }
                        };
                    }
                ]);
            })(window.angular);

Our authentication-service (as mentioned in tutorial or many posts):

@RestController
public class UserController {
    @RequestMapping(value = "/user")
    public Principal user(Principal user) {
        return user;
    }
}

The SecurityWebAppInitializer with a custom filter that shall log the IP and username.

@Order(2)
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
    @Override
    protected void afterSpringSecurityFilterChain(ServletContext servletContext) {
        super.beforeSpringSecurityFilterChain(servletContext);
        insertFilters(servletContext,new MultipartFilter(),new MDCFilter());
    }
}

Finally our Spring Security config

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;

@Configuration
@EnableWebSecurity(debug=true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    DataSource dataSource;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
        .jdbcAuthentication()
             .dataSource(dataSource)
                .usersByUsernameQuery(
                        "select email,pwHash,true from user where email = ?")
                .authoritiesByUsernameQuery(
                        "select email, rolle_rollenname from user where email = ?");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/user", "/login", "/logout", "login.html").permitAll()
            .anyRequest().authenticated()
        .and()
            .csrf().csrfTokenRepository(csrfTokenRepository())
        .and()
            .addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
        .formLogin()
            .loginPage("/login")
            //.logoutSuccessHandler(new customLogoutSuccessHandler())
            .and()
        .logout()
            .logoutUrl("/logout");
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
         web
            .ignoring()
            .antMatchers("/scripts/**")
            .antMatchers("/stylesheets/**");
    }

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

When using custom authentication and the default loginpage it works without problems. Maybe the login.html or login.js is wrong ...

UPDATE

When I use .httpBasic() without specifying the loginform a browser dialog appears when I try to access a secured ressource. I want a redirect to the custom login page instead of the browser dialog. How to do?

好吧,我通过使用JSON Web Tokens,一个自定义无状态过滤器并在每次返回前端时将令牌提供给他们,当他们要求时。

Try this :

.factory('AuthFactory', ['$http', 'contextPath', '$q', '$timeout', function ($http, contextPath, $q, $timeout) {

            function User() {
            };

            var currentUser = null;

            var userChangeCallbacks = [];

            var notifyUserChange = function (newUser) {
                angular.forEach(userChangeCallbacks, function (callback) {
                    $timeout(function () {
                        callback(newUser);
                    });
                });
            };

            var exported = {
                getCurrentUser: function () {
                    return currentUser;
                },
                refresh: function () {
                    return $q(function (resolve, reject) {
                        //Get the current user
                        $http.get(contextPath + '/rest/user/current')
                                .success(function (data) {
                                    currentUser = new User();
                                    for (var key in data) {
                                        currentUser[key] = data[key];
                                    }
                                    notifyUserChange(currentUser);
                                    resolve(currentUser);
                                })
                    });
                },
                registerUserChangeHandler: function (callback) {
                    console.log("registered handler: " + callback);
                    userChangeCallbacks.push(callback);
                }
            };

            return exported;
        }]);

Then call that refresh method in your login controller.

LOGIN CONTROLLER

$scope.login = function (username, password) {
            UserService.login({
                'username': username,
                'password': password
            }, function () {
                AuthFactory.refresh();
                $state.go("home");
            });
        };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM