AngularJS + Spring 安全记住我功能

Posted

技术标签:

【中文标题】AngularJS + Spring 安全记住我功能【英文标题】:AngularJS + Spring security remember me feature 【发布时间】:2017-08-10 18:17:57 【问题描述】:

我正在做基于 spring boot + Angularjs 的应用程序 我根据这篇博文进行的身份验证:https://spring.io/guides/tutorials/spring-security-and-angular-js/ 所以我启用了基本用户/密码或 OAuth2

我想向它添加记住我的功能。我有 AuthService

AuthService.authenticate = function (credentials, callback) 
    var headers = credentials ? 
        authorization: "Basic "
        + btoa(credentials.username + ":" + credentials.password)
     : ;

    $http.get('api/user/', headers: headers, timeout: 5000).then(
        function (response) 
            var data = response.data;
            if (data.id) 
                $rootScope.authenticated = true;
                $rootScope.principal = data;
                $translate.use($rootScope.principal.language);
                $location.search('lang', $rootScope.principal.language);
                AvatarService.getUserAvatar($rootScope.principal);
                $log.debug("[DEBUG] User logged in " + $rootScope.principal.id);
             else 
                $rootScope.authenticated = false;
            
            callback && callback();
        ,
        function () 
            $rootScope.authenticated = false;
            callback && callback();
        );
;

在登录控制器中我得到了处理:

$scope.credentials = ;
//LOGIN
$scope.login = function () 
    AuthService.authenticate($scope.credentials, function () 
        if ($rootScope.authenticated) 
            $location.path("/");
            AlertService.clearAlerts();
         else 
            $location.path("/login");
            AlertService.addError('user.login.failed');
        
    );
;

在 Spring 安全性上,我像往常一样设置它,(配置的一部分)

            ....
            .and().formLogin()
                .loginPage("/#/login")
                .and().rememberMe()
                .rememberMeServices(rememberMeServices())
                .key("remember-me-key")
            .and().addFilterBefore(new CsrfHeaderFilter(), CsrfFilter.class)
                .csrf()
            ....

但我的猜测是它期望使用记住我而不是基本身份验证的后调用

如何调整它才能使用记住我? 我可以通过帖子执行调用以使用 j_username j_password 登录并记住我吗?

【问题讨论】:

【参考方案1】:

我也在开发 Spring + Angular2 应用程序。就我而言,后端返回 2 个令牌(访问令牌和刷新令牌)。之后,我在每个请求的标题中添加“授权”:“Bearer”+this.accessToken。

【讨论】:

【参考方案2】:

您可以配置 Spring Boot 服务器以将身份验证令牌发回给客户端。在客户端(Angular)中,将令牌保存在某处(本地存储/cookie)。然后您可以在应用首次加载时检查令牌的存在。 JWT 令牌在处理 Web 客户端身份验证时非常有用。

例如,如果我的安全配置文件中有这段代码。

.authorizeRequests()
    .antMatchers("/", "/index.html", "/login.html", "/home.html").permitAll()
.anyRequest()
.authenticated().and()
.formLogin()
    .successHandler(authenticationSuccessHandler)
    .failureHandler(authenticationFailureHandler)

我可以轻松实现authenticationSuccessHandler 并在成功登录后向用户发出一个身份验证令牌或身份验证cookie,或两者都返回给用户。

public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler 

... bla bla bla

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
        Authentication authentication ) throws IOException, ServletException 
    clearAuthenticationAttributes(request);
    User user = (User)authentication.getPrincipal();

    String jws = tokenHelper.generateToken( user.getUsername() );

    // cookie in response
    Cookie authCookie = new Cookie( TOKEN_COOKIE, ( jws ) );
    authCookie.setPath( "/" );
    authCookie.setHttpOnly( true );
    authCookie.setMaxAge( EXPIRES_IN );
    response.addCookie( authCookie );
    // token in response
    UserTokenState userTokenState = new UserTokenState(jws, EXPIRES_IN);
    String jwtResponse = objectMapper.writeValueAsString( userTokenState );
    response.setContentType("application/json");
    response.getWriter().write( jwtResponse );

更多详情请见springboot-jwt-starter。这是一个使用 Spring Boot 和 AngularJS 的入门工具包项目。

如果您想使用 Angular 实现 Spring Boot 安全性,The Login Page: Angular JS and Spring Security Part II 系列是一本非常好的阅读材料。

【讨论】:

The Login Page: Angular JS and Spring Security Part II 完全基于我所做的应用程序,因此我得到了覆盖。将很快使用身份验证令牌检查解决方案 springboot-jwt-starter 基于“登录页面:Angular JS 和 Spring Security Part II”,但它不使用会话,而是使用 JWT(JSON Web 令牌)。如果您还有其他问题,请告诉我。 谢谢。根据您的 github 代码,我能够调整我的应用程序,使其使用令牌并将其保存 1 周。适用于普通和誓言登录 亲爱的,我很高兴听到我的代码有帮助。你介意给它一个星号来支持 github 代码吗? 当然 :) 一句话:在 TokenFilter 上,每个请求都会对 DB 进行查询,这在性能方面并不好。在我的应用程序中,我添加了会话(从配置中删除了无状态),并且仅当 SecurityContextHolder.getContext().getAuthentication() 或 instanceof AnonAuthentication 时才从数据库中获取令牌值和帐户。作为额外的好处,auth 有当前帐户,调用 whoami 时可以直接返回。同样在我的应用程序中,我将整个主体存储在 angular $rootContext.principal 中。这样就可以读取所有信息,如 mail 、 id 或 avatar id ,而无需对后端进行额外查询【参考方案3】:

您可以使用带有 Angular 和基本身份验证的 Spring remember-me 服务,您必须在帖子中发送 remember-me 参数。

下面是 Angular2 的示例:

import Injectable from "@angular/core";
import HttpClient, HttpHeaders, HttpParams from "@angular/common/http";

import Observable from "rxjs/Observable";
import CSRF_URL, LOGIN_URL, LOGOUT_URL from "./auth.constant";

export interface RespAccess 
  email?: String;
  name: string;
  permissions: [string];


@Injectable()
export class AuthenticationService 

  constructor(private http: HttpClient) 

  login(username: string, password: string, rememberme: boolean): Observable<RespAccess> 
    let headers = new HttpHeaders();
    headers = headers.append('Authorization', 'Basic ' + btoa(username + ':' + password));
    headers = headers.append('X-Requested-With', 'XMLHttpRequest'); // to suppress 401 browser popup

    const params = new HttpParams().append('remember-me', (rememberme ? 'true' : 'false' ));
    return this.http.post<RespAccess>(LOGIN_URL,'', headers, params);
  

  logout() : Observable<any> 
    return this.http.post<any>(LOGOUT_URL, '');
  

  getCSRFToken() 
    this.http.get<any>(CSRF_URL) // /api/auth/csrf
      .subscribe();
  

如果您使用 CSRF 保护,您必须在登录前获取 CSRF 令牌。您可以简单地访问返回令牌的不安全 url,这是 Spring 服务器端控制器中的方法:

@RequestMapping("/api/auth/csrf")
public CsrfToken csrf(CsrfToken token) 
    return token;

【讨论】:

嗨@Carlos Cuesta,即使记住我的参数是假的,也会创建cookie。

以上是关于AngularJS + Spring 安全记住我功能的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot - 具有安全性的 Angularjs $stateProvider

使用 angularjs 使用安全的 spring rest 服务

AngularJS 和 Spring MVC 安全性

spring安全管理网关和angularjs异常

使用 angularJS 的 Spring Boot 安全性/登录身份验证

如何为单页 AngularJS 应用程序实现基本的 Spring 安全性(会话管理)