RESTful API (Jersey) を使用する AngularJS Web アプリを構築しています。
サーバー側では、Java アプリケーション サーバー (詳細には Glassfish 4) を使用しています。
私のセットアップは次のとおりです。
- AngularJS webapp は、単一の war ファイルとして Java EE アプリケーション サーバーにデプロイされます。
- RESTfulAPI (およびバックエンド ロジック) も war ファイルとして同じ Java EE アプリケーション サーバーにデプロイされます。
- AngularJS Web アプリケーションは、REST API を呼び出して必要なデータを取得します。
- RESTful API:
/api/public/*
制限されていないパブリック アクセス用 (例: /api/public/users/signup で新しいユーザーとして登録)。すべてのユーザーがこの領域にアクセスできます。ログイン不要/api/private/*
制限付きアクセス用 (例: /api/private/account/{id} でアカウント固有のデータを取得)
- プライベート リソースは、Java EE 内部セキュリティの概念で保護されます (web.xml の詳細については、以下を参照してください)。
Web.xml
<security-constraint>
<web-resource-collection>
<web-resource-name>PRIVATE REST API</web-resource-name>
<url-pattern>/private/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>HEAD</http-method>
<http-method>PUT</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<description>Have to be a USER</description>
<role-name>USERS</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>userauth</realm-name>
</login-config>
<security-role>
<description/>
<role-name>USERS</role-name>
</security-role>
このセットアップは私にとってはうまくいきますが、ブラウザで手動で URL を呼び出す場合に限られます。私が認証されず、保護された領域にアドレス指定すると、ブラウザーはユーザー名とパスワードを要求します。提供された資格情報が有効であれば、そこから制限区域にアクセスできます: わかりました。
しかし、それを AngularJS で動作させるにはどうすればよいでしょうか? ログインするには、POST API 呼び出し/api/public/users/login
があります。フォームから資格情報 (ユーザー名とパスワード) を提供する必要があります。
クライアント側のコードは次のとおりです。
ctrls.controller('LoginController', function($scope, $http, $log, $location, UserService, RemoteServerHelperService) {
$scope.loginDataWrong = undefined;
$scope.login = function(credentials) {
$http.post(RemoteServerHelperService.buildURL('/public/users/login'), credentials)
.success(function (data, status) {
UserService.setLoggedIn(credentials.email);
$scope.loginDataWrong = undefined;
$location.path('/app');
$log.info("login attempt: successfull. User.loggedIn:" + UserService.isLoggedIn());
}).error(function (data, status, headers, config) {
UserService.invalidate();
$scope.loginDataWrong = true;
$log.info("login attempt: failed. User.loggedIn:" + UserService.isLoggedIn());
});
};
});
クライアント側のコードは機能しているようです。また、クライアント側でコンテンツを保護するためのルートもいくつか用意しています。クライアント側のコードを詳細に説明する投稿がいくつかあります。したがって、この「スニペット」で十分です。
サーバー側のコードは次のとおりです。
@POST
@Path("/login")
@Consumes(MediaType.APPLICATION_JSON)
public Response login(Credentials credentials, @Context HttpServletRequest request) {
if (isAlreadyAuthenticated(request)) {
try {
request.logout();
} catch (ServletException ex) {
return Response.status(Status.CONFLICT).build();
}
}
// not authenticated
try {
request.login(credentials.getEmail(), credentials.getPassword());
return Response.ok().build();
} catch (ServletException ex) {
return Response.status(Status.CONFLICT).build();
}
}
ただし、これは、以降の要求に対してクライアント側を「認証」しません。ログインに成功した後、制限されたエリアで API 呼び出しを行うと403 FORBIDDEN
、サーバー側から応答が返ってきます。だから私は何か間違ったことをしていると思います。さらなるリクエストのためにクライアントをサーバーに認証する方法について何か考えはありますか?
考えられる解決策は、FORM ベースの認証に切り替えて、j_username と j_password を使用して j_security_check を呼び出すことですが、今のところ、BASIC 認証を使用し、RESTful API を介して手動で認証を実行したいと考えています。
どういうわけか、設定$httpProvider.defaults.withCredentials = true;
は何の効果もないようです。
アップデート:
ロスリーダーのヒントのおかげで、私は問題を解決しました。login()
Java EE-Server に認証を任せたいので、メソッドを削除しました。
セキュリティで保護された領域にアクセスするには、AngularJS Web アプリケーションで HTTP ヘッダーを正しく設定する必要があります。私の場合$http.defaults.headers.common['Authorization'] = 'Basic <username:pw>';
、username:password
Base64 でエンコードする必要があります。
ヘッダーを正しく設定した後、パスワード プロンプトは表示されず、AngularJS Web アプリケーションは REST API にアクセスできます。