web-dev-qa-db-ja.com

Dropwizardでリソースの基本認証を行う方法

基本認証は機能していると思いますが、ユーザーがサインインしている場合にのみリソースにアクセスできるようにリソースを保護する方法がわかりません。

public class SimpleAuthenticator implements Authenticator<BasicCredentials, User> {
    UserDAO userDao;

    public SimpleAuthenticator(UserDAO userDao) {this.userDao = userDao;}

    @Override
    public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException    
    {
        User user = this.userDao.getUserByName(credentials.getUsername());
        if (user!=null &&
                user.getName().equalsIgnoreCase(credentials.getUsername()) &&
                BCrypt.checkpw(credentials.getPassword(), user.getPwhash())) {
            return Optional.of(new User(credentials.getUsername()));
        }
        return Optional.absent();
    }
}

私のサインインリソースは次のようなものです:

@Path("/myapp")
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {
    @GET
    @Path("/signin")
    public User signin(@Auth User user) {
        return user;
    }
}

そして、私はユーザーに次のように署名します:

~/Java/myservice $ curl -u "someuser" http://localhost:8080/myapp/signin
Enter Host password for user 'someuser':
{"name":"someuser"}

質問

ユーザーが/myapp/signinエンドポイントを使用してブラウザーまたはネイティブモバイルアプリのフロントエンドからサインインするとします。次に、別のエンドポイントを保護するにはどうすればよいですか。たとえば、/myapp/{username}/getstuff which ユーザーがサインインする必要があります

@GET
@Path("/myapp/{username}/getstuff")
public Stuff getStuff(@PathParam("username") String username) {
    //some logic here
    return new Stuff();
}
16
birdy

RESTを実装しようとすると、2つのことがあります。 1つは認証(あなたはそれを機能させているようです)であり、もう1つは承認(あなたの質問は私が信じていることです)です。

以前にdropwizardで処理した方法は、ユーザーがサインインするたびに、ある種のaccess_token(認証されたことを証明します)をクライアントに返します。これは、一部のユーザーが連続して呼び出すたびに返される必要があります。ヘッダー(通常、これは「Authorization」ヘッダーを介して行われます)。サーバー側では、クライアントに返す前に、このaccess_tokenをそのユーザーに保存/マップする必要があります。その後のすべての呼び出しがそのaccess_tokenで行われると、そのaccess_tokenでマップされたユーザーを検索し、そのユーザーかどうかを判断します。そのリソースへのアクセスが許可されているかどうか。今例:

1)ユーザーは/ myapp/signinでサインインします

2)ユーザーを認証し、access_token-> userIdABCDのように、同じものを自分の側に保存しながら、応答としてaccess_tokenを送り返します。

3)クライアントが/ myapp/{username}/getstuffに戻ります。クライアントが指定したaccess_tokenで「Authorization」ヘッダーを提供していない場合は、すぐに401Unauthorizedコードを返す必要があります。

4)クライアントがaccess_tokenを提供している場合は、手順2で保存したaccess_tokenに基づいてユーザーを検索し、そのuserIdがそのリソースにアクセスできるかどうかを確認できます。そうでない場合は、401の無許可のコードを返し、アクセスできる場合は、実際のデータを返します。

「Authorization」ヘッダー部分が登場します。 「@ContextHttpServletRequesthsr」パラメーターを使用して、すべての呼び出しで「Authoroziation」ヘッダーにアクセスできますが、各呼び出しにそのパラメーターを追加することは意味がありますか?いいえ、そうではありません。これは、セキュリティフィルターがdropwizardで役立つ場所です。セキュリティフィルターを追加する方法の例を次に示します。

public class SecurityFilter extends OncePerRequestFilter{
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{
String accessToken = request.getHeader("Authorization");
// Do stuff here based on the access token (check for user's authorization to the resource ...
}

では、このセキュリティフィルタは実際にどのリソースを保護しますか?そのためには、保護したい特定のリソースにこのフィルターを追加する必要があります。これは次のように実行できます。

environment.addFilter(SecurityFilter, "/myapp/*");

ここで、URL/myapp/signinと/ myapp/{username}/getstuffの両方がこのセキュリティフィルターを通過することを覚えておいてください。ただし、/ myapp/signinにはaccess_tokenがありません。これは、明らかに何も指定していないためです。まだクライアントに。これは、次のようにフィルター自体で処理する必要があります。

String url = request.getRequestURL().toString();
if(url.endsWith("signin"))
{
// Don't look for authorization header, and let the filter pass without any checks
}
else
{
// DO YOUR NORMAL AUTHORIZATION RELATED STUFF HERE
}

保護するURLは、URLの構造と保護する対象によって異なります。設計するURLが優れているほど、保護のためのセキュリティフィルターを簡単に作成できます。このセキュリティフィルターを追加すると、フローは次のようになります。

1)ユーザーは/ myapp/signinに移動します。呼び出しはフィルターを通過し、その「if」ステートメントのために、/ myapp/signinの実際のリソースに続行され、成功した認証に基づいてaccess_tokenが割り当てられます。

2)ユーザーがaccess_tokenを使用して/ myapp/{username}/mystuffを呼び出します。この呼び出しは同じセキュリティフィルターを通過し、実際に認証を行う「else」ステートメントを通過します。承認が通過した場合、呼び出しは実際のリソースハンドラーに対して続行され、承認されていない場合は、401が返されます。

public class SecurityFilter extends OncePerRequestFilter
{

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
    {
        String url = request.getRequestURL().toString();
        String accessToken = request.getHeader("Authorization");
        try
        {
            if (accessToken == null || accessToken.isEmpty())
            {
                throw new Exception(Status.UNAUTHORIZED.getStatusCode(), "Provided access token is either null or empty or does not have permissions to access this resource." + accessToken);
            }
            if (url.endsWith("/signin"))
            {
    //Don't Do anything
                filterChain.doFilter(request, response);
            }
            else
            {
    //AUTHORIZE the access_token here. If authorization goes through, continue as normal, OR throw a 401 unaurhtorized exception

                filterChain.doFilter(request, response);
            }
        }
        catch (Exception ex)
        {
            response.setStatus(401);
            response.setCharacterEncoding("UTF-8");
            response.setContentType(MediaType.APPLICATION_JSON);
            response.getWriter().print("Unauthorized");
        }
    }
}

これがお役に立てば幸いです。これを自分で理解するのに約2日かかりました!

26
shahshi15

単純なユーザーでごめんなさい。 @Authユーザーユーザーを使用してリソースを保護できると思います

public Service1Bean Service1Method1(
    @Auth User user,
    @QueryParam("name") com.google.common.base.Optional<String> name) {