JAX-RSおよびJerseyを使用したRESTトークンベースの認証のベストプラクティス


459

ジャージーでトークンベースの認証を有効にする方法を探しています。特定のフレームワークを使用しないようにしています。それは可能ですか?

私の計画は、ユーザーがWebサービスにサインアップし、Webサービスがトークンを生成してクライアントに送信し、クライアントがそれを保持することです。次に、クライアントは、要求ごとに、ユーザー名とパスワードの代わりにトークンを送信します。

リクエストごとにカスタムフィルターを使用する@PreAuthorize("hasRole('ROLE')") ことを考えていましたが、これにより、データベースへの多くのリクエストにより、トークンが有効であるかどうかが確認されると思いました。

または、フィルターを作成せず、各リクエストにparamトークンを入れますか?そのため、各APIはまずトークンをチェックし、その後、リソースを取得するために何かを実行します。

回答:


1388

トークンベースの認証の仕組み

トークンベースの認証では、クライアントはトークンと呼ばれるデータの一部のハード認証情報(ユーザー名やパスワードなど)を交換します。リクエストごとに、クライアントはハード認証情報を送信する代わりに、トークンをサーバーに送信して、認証と承認を実行します。

簡単に言うと、トークンに基づく認証スキームは次の手順に従います。

  1. クライアントは資格情報(ユーザー名とパスワード)をサーバーに送信します。
  2. サーバーは資格情報を認証し、資格情報が有効な場合は、ユーザーのトークンを生成します。
  3. サーバーは、以前に生成されたトークンをユーザーIDと有効期限と共に何らかのストレージに保存します。
  4. サーバーは、生成されたトークンをクライアントに送信します。
  5. クライアントは各リクエストでトークンをサーバーに送信します。
  6. サーバーは、各要求で、着信要求からトークンを抽出します。トークンを使用して、サーバーはユーザーの詳細を検索して認証を実行します。
    • トークンが有効な場合、サーバーは要求を受け入れます。
    • トークンが無効な場合、サーバーは要求を拒否します。
  7. 認証が実行されると、サーバーは承認を実行します。
  8. サーバーは、トークンを更新するエンドポイントを提供できます。

注:サーバーが署名済みトークン(ステートレス認証を実行できるJWTなど)を発行した場合、ステップ3は不要です。

JAX-RS 2.0(Jersey、RESTEasy、Apache CXF)でできること

このソリューションはJAX-RS 2.0 APIのみを使用し、ベンダー固有のソリューションを回避します。したがって、JerseyRESTEasyApache CXFなどのJAX-RS 2.0実装で動作するはずです。

トークンベースの認証を使用している場合は、サーブレットコンテナによって提供され、アプリケーションのweb.xml記述子を介して構成可能な標準のJava EE Webアプリケーションセキュリティメカニズムに依存していないことに言及することは価値があります。これはカスタム認証です。

ユーザー名とパスワードを使用してユーザーを認証し、トークンを発行する

資格情報(ユーザー名とパスワード)を受け取って検証し、ユーザーにトークンを発行するJAX-RSリソースメソッドを作成します。

@Path("/authentication")
public class AuthenticationEndpoint {

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Response authenticateUser(@FormParam("username") String username, 
                                     @FormParam("password") String password) {

        try {

            // Authenticate the user using the credentials provided
            authenticate(username, password);

            // Issue a token for the user
            String token = issueToken(username);

            // Return the token on the response
            return Response.ok(token).build();

        } catch (Exception e) {
            return Response.status(Response.Status.FORBIDDEN).build();
        }      
    }

    private void authenticate(String username, String password) throws Exception {
        // Authenticate against a database, LDAP, file or whatever
        // Throw an Exception if the credentials are invalid
    }

    private String issueToken(String username) {
        // Issue a token (can be a random String persisted to a database or a JWT token)
        // The issued token must be associated to a user
        // Return the issued token
    }
}

資格情報の検証時に例外がスローされた場合、ステータス403(禁止)の応答が返されます。

資格情報が正常に検証200されると、ステータス(OK)の応答が返され、発行されたトークンが応答ペイロードでクライアントに送信されます。クライアントはすべてのリクエストでトークンをサーバーに送信する必要があります。

を使用する場合application/x-www-form-urlencoded、クライアントはリクエストペイロードで次の形式で認証情報を送信する必要があります。

username=admin&password=123456

フォームのパラメータの代わりに、ユーザー名とパスワードをクラスにラップすることが可能です:

public class Credentials implements Serializable {

    private String username;
    private String password;

    // Getters and setters omitted
}

そして、それをJSONとして消費します。

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response authenticateUser(Credentials credentials) {

    String username = credentials.getUsername();
    String password = credentials.getPassword();

    // Authenticate the user, issue a token and return a response
}

このアプローチを使用する場合、クライアントは、リクエストのペイロードで次の形式で認証情報を送信する必要があります。

{
  "username": "admin",
  "password": "123456"
}

リクエストからトークンを抽出して検証する

クライアントはAuthorization、リクエストの標準HTTP ヘッダーでトークンを送信する必要があります。例えば:

Authorization: Bearer <token-goes-here>

標準のHTTPヘッダーの名前は、承認ではなく認証情報を運ぶため、残念です。ただし、サーバーに資格情報を送信するための標準のHTTPヘッダーです。

JAX-RSは@NameBinding、フィルターとインターセプターをリソースクラスとメソッドにバインドする他の注釈を作成するために使用されるメタ注釈を提供します。次のように@Secured注釈を定義します。

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured { }

上記で定義された名前バインディング注釈は、を実装するフィルタークラスを修飾するために使用さContainerRequestFilterれ、リソースメソッドで処理される前に要求をインターセプトできるようにします。をContainerRequestContext使用して、HTTPリクエストヘッダーにアクセスし、トークンを抽出できます。

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    private static final String REALM = "example";
    private static final String AUTHENTICATION_SCHEME = "Bearer";

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Get the Authorization header from the request
        String authorizationHeader =
                requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);

        // Validate the Authorization header
        if (!isTokenBasedAuthentication(authorizationHeader)) {
            abortWithUnauthorized(requestContext);
            return;
        }

        // Extract the token from the Authorization header
        String token = authorizationHeader
                            .substring(AUTHENTICATION_SCHEME.length()).trim();

        try {

            // Validate the token
            validateToken(token);

        } catch (Exception e) {
            abortWithUnauthorized(requestContext);
        }
    }

    private boolean isTokenBasedAuthentication(String authorizationHeader) {

        // Check if the Authorization header is valid
        // It must not be null and must be prefixed with "Bearer" plus a whitespace
        // The authentication scheme comparison must be case-insensitive
        return authorizationHeader != null && authorizationHeader.toLowerCase()
                    .startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
    }

    private void abortWithUnauthorized(ContainerRequestContext requestContext) {

        // Abort the filter chain with a 401 status code response
        // The WWW-Authenticate header is sent along with the response
        requestContext.abortWith(
                Response.status(Response.Status.UNAUTHORIZED)
                        .header(HttpHeaders.WWW_AUTHENTICATE, 
                                AUTHENTICATION_SCHEME + " realm=\"" + REALM + "\"")
                        .build());
    }

    private void validateToken(String token) throws Exception {
        // Check if the token was issued by the server and if it's not expired
        // Throw an Exception if the token is invalid
    }
}

トークンの検証中に問題が発生した場合、ステータスが401(未承認)の応答が返されます。それ以外の場合、リクエストはリソースメソッドに進みます。

RESTエンドポイントの保護

認証フィルターをリソースメソッドまたはリソースクラスにバインドするには、@Secured上記で作成したアノテーションでアノテーションを付けます。アノテーションが付けられているメソッドやクラスの場合、フィルターが実行されます。つまり、リクエストが有効なトークンで実行された場合にのみ、そのようなエンドポイントに到達します。

一部のメソッドまたはクラスが認証を必要としない場合は、単にそれらに注釈を付けないでください。

@Path("/example")
public class ExampleResource {

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response myUnsecuredMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Secured
        // The authentication filter won't be executed before invoking this method
        ...
    }

    @DELETE
    @Secured
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response mySecuredMethod(@PathParam("id") Long id) {
        // This method is annotated with @Secured
        // The authentication filter will be executed before invoking this method
        // The HTTP request must be performed with a valid token
        ...
    }
}

上記の例では、アノテーションが付けられているため、フィルターはメソッドに対してのみ実行されます。mySecuredMethod(Long)@Secured

現在のユーザーの特定

REST APIに対して、リクエストを実行しているユーザーを知る必要がある可能性が非常に高いです。次のアプローチを使用してそれを実現できます。

現在のリクエストのセキュリティコンテキストのオーバーライド

ContainerRequestFilter.filter(ContainerRequestContext)メソッド内SecurityContextで、現在のリクエストに対して新しいインスタンスを設定できます。次に、をオーバーライドしてSecurityContext.getUserPrincipal()Principalインスタンスを返します。

final SecurityContext currentSecurityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(new SecurityContext() {

        @Override
        public Principal getUserPrincipal() {
            return () -> username;
        }

    @Override
    public boolean isUserInRole(String role) {
        return true;
    }

    @Override
    public boolean isSecure() {
        return currentSecurityContext.isSecure();
    }

    @Override
    public String getAuthenticationScheme() {
        return AUTHENTICATION_SCHEME;
    }
});

トークンを使用して、Principalの名前になるユーザー識別子(username)を検索します。

SecurityContextを任意のJAX-RSリソースクラスに挿入します。

@Context
SecurityContext securityContext;

JAX-RSリソースメソッドでも同じことができます。

@GET
@Secured
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response myMethod(@PathParam("id") Long id, 
                         @Context SecurityContext securityContext) {
    ...
}

そして、次を取得しPrincipalます:

Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();

CDI(Context and Dependency Injection)の使用

なんらかの理由でをオーバーライドしたくない場合はSecurityContext、イベントやプロデューサーなどの便利な機能を提供するCDI(Context and Dependency Injection)を使用できます。

CDI修飾子を作成します。

@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER })
public @interface AuthenticatedUser { }

AuthenticationFilter上記で作成したものに、次のようにEvent注釈を付けて挿入します@AuthenticatedUser

@Inject
@AuthenticatedUser
Event<String> userAuthenticatedEvent;

認証が成功した場合、パラメーターとしてユーザー名を渡してイベントを発生させます(ユーザーに対してトークンが発行され、トークンはユーザーIDの検索に使用されます)。

userAuthenticatedEvent.fire(username);

アプリケーションにユーザーを表すクラスがある可能性が非常に高いです。このクラスを呼び出しましょうUser

認証イベントを処理するCDI Beanを作成Userし、対応するユーザー名を持つインスタンスを見つけて、それをauthenticatedUserプロデューサーフィールドに割り当てます。

@RequestScoped
public class AuthenticatedUserProducer {

    @Produces
    @RequestScoped
    @AuthenticatedUser
    private User authenticatedUser;

    public void handleAuthenticationEvent(@Observes @AuthenticatedUser String username) {
        this.authenticatedUser = findUser(username);
    }

    private User findUser(String username) {
        // Hit the the database or a service to find a user by its username and return it
        // Return the User instance
    }
}

このauthenticatedUserフィールドは、UserJAX-RSサービス、CDI Bean、サーブレット、EJBなどのコンテナ管理Beanに注入できるインスタンスを生成します。次のコードを使用してUserインスタンスを挿入します(実際には、CDIプロキシです)。

@Inject
@AuthenticatedUser
User authenticatedUser;

CDI @ProducesアノテーションはJAX-RS アノテーションとは異なることに注意してください@Produces

必ずBeanでCDI @Producesアノテーションを使用してくださいAuthenticatedUserProducer

ここで重要なのは、アノテーションが付けられたBeanです。これにより@RequestScoped、フィルターとBeanの間でデータを共有できます。イベントを使用したくない場合は、フィルターを変更して、認証されたユーザーをリクエストスコープのBeanに格納し、JAX-RSリソースクラスから読み取ることができます。

をオーバーライドするアプローチと比較してSecurityContext、CDIアプローチでは、JAX-RSリソースおよびプロバイダー以外のBeanから認証済みユーザーを取得できます。

役割ベースの許可のサポート

ロールベースの承認をサポートする方法の詳細については、他の回答を参照してください。

トークンの発行

トークンには次のものがあります。

  • 不透明:値自体(ランダムな文字列など)以外の詳細を明らかにしない
  • 自己完結型:トークン自体に関する詳細(JWTなど)が含まれています。

以下の詳細を参照してください。

トークンとしてのランダムな文字列

トークンは、ランダムな文字列を生成し、ユーザー識別子と有効期限と共にデータベースに永続化することで発行できます。Javaでランダムな文字列を生成する方法の良い例は、こちらにあります。あなたも使うことができます:

Random random = new SecureRandom();
String token = new BigInteger(130, random).toString(32);

JWT(JSON Webトークン)

JWT(JSON Web Token)は、2つのパーティ間でクレームを安全に表すための標準的な方法であり、RFC 7519で定義されています

これは自己完結型のトークンであり、クレームに詳細を格納できます。これらのクレームは、Base64としてエンコードされたJSONであるトークンペイロードに格納されます。RFC 7519に登録されているいくつかのクレームとその意味(詳細については、完全なRFCをお読みください):

  • iss:トークンを発行したプリンシパル。
  • sub:JWTの主体であるプリンシパル。
  • exp:トークンの有効期限。
  • nbf:トークンが処理のために受け入れられ始める時間。
  • iat:トークンが発行された時刻。
  • jti:トークンの一意の識別子。

パスワードなどの機密データをトークンに保存してはいけないことに注意してください。

ペイロードはクライアントで読み取ることができ、トークンの整合性はサーバーで署名を確認することで簡単に確認できます。署名は、トークンが改ざんされないようにするものです。

追跡する必要がない場合は、JWTトークンを永続化する必要はありません。とはいえ、トークンを永続化することにより、トークンへのアクセスを無効にして取り消す可能性があります。サーバー上でトークン全体を永続化する代わりに、JWTトークンを追跡するために、トークン識別子(jtiクレーム)を、トークンを発行したユーザー、有効期限などの他の詳細とともに永続化できます。

トークンを永続化する場合は、データベースが無制限に大きくなるのを防ぐために、常に古いトークンを削除することを検討してください。

JWTの使用

次のようなJWTトークンを発行および検証するJavaライブラリがいくつかあります。

JWTを操作するためのその他の優れたリソースを見つけるには、http://jwt.ioを参照してください。

JWTによるトークン失効の処理

トークンを取り消す場合は、トークンを追跡する必要があります。トークン全体をサーバー側に保存する必要はありません。トークン識別子(一意である必要があります)と、必要に応じて一部のメタデータのみを保存します。トークン識別子にはUUIDを使用できます。

jti前記トークンにトークン識別子を格納するために使用されるべきです。トークンを検証するときjtiは、サーバー側にあるトークン識別子に対してクレームの値をチェックして、トークンが取り消されていないことを確認してください。

セキュリティ上の理由から、ユーザーがパスワードを変更したときに、ユーザーのすべてのトークンを取り消します。

追加情報

  • どのタイプの認証を使用するかは関係ありません。中間者攻撃を防ぐために、常に HTTPS接続の上で実行してください。
  • 見てみましょう、この質問のトークンの詳細については、情報セキュリティのを。
  • この記事では、トークンベースの認証に関する役立つ情報を紹介します。

The server stores the previously generated token in some storage along with the user identifier and an expiration date. The server sends the generated token to the client. このRESTfulはどうですか?
scottysseus 2018年

3
@scottyseusトークンベースの認証は、サーバーが発行したトークンをサーバーが記憶する方法によって機能します。ステートレス認証にJWTトークンを使用できます。
cassiomolin

プレーン(サーバー生成ノンスでハッシュされた)の代わりにハッシュされたパスワードを送信するのはどうですか?セキュリティレベルが向上しますか(たとえば、httpsを使用しない場合)?真ん中の男の場合-彼は1つのセッションをハイジャックできますが、少なくともパスワードは取得できません
デニスイツコビッチ

15
これが公式ドキュメントにないことは信じられません。
Daniel M.

2
@grep RESTでは、サーバー側のセッションなどはありません。したがって、セッション状態はクライアント側で管理されます。
カシオモリン

98

この回答はすべて許可に関するものであり認証に関する私の以前の回答の補足です

なぜ別の答えですか?JSR-250アノテーションをサポートする方法の詳細を追加して、以前の回答を拡張しようとしました。ただし、元の回答が長くなりすぎて最大長の30,000文字を超えました。そのため、認証の詳細全体をこの回答に移動し、他の回答は認証の実行とトークンの発行に焦点を合わせたままにしました。


@Securedアノテーションを使用したロールベースの承認のサポート

他の回答に示されている認証フローに加えて、RESTエンドポイントではロールベースの承認をサポートできます。

列挙を作成し、必要に応じて役割を定義します。

public enum Role {
    ROLE_1,
    ROLE_2,
    ROLE_3
}

@Secured前に作成した名前バインディングアノテーションを変更して、ロールをサポートします。

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured {
    Role[] value() default {};
}

そして@Secured、承認を実行するためにリソースクラスとメソッドに注釈を付けます。メソッドアノテーションはクラスアノテーションをオーバーライドします。

@Path("/example")
@Secured({Role.ROLE_1})
public class ExampleResource {

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response myMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Secured
        // But it's declared within a class annotated with @Secured({Role.ROLE_1})
        // So it only can be executed by the users who have the ROLE_1 role
        ...
    }

    @DELETE
    @Path("{id}")    
    @Produces(MediaType.APPLICATION_JSON)
    @Secured({Role.ROLE_1, Role.ROLE_2})
    public Response myOtherMethod(@PathParam("id") Long id) {
        // This method is annotated with @Secured({Role.ROLE_1, Role.ROLE_2})
        // The method annotation overrides the class annotation
        // So it only can be executed by the users who have the ROLE_1 or ROLE_2 roles
        ...
    }
}

前に定義しAUTHORIZATIONAUTHENTICATION優先度フィルターの後に実行される優先度を持つフィルターを作成します。

ResourceInfo使用して、要求を処理するリソースMethodとリソースを取得し、それらから注釈をClass抽出@Securedできます。

@Secured
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Get the resource class which matches with the requested URL
        // Extract the roles declared by it
        Class<?> resourceClass = resourceInfo.getResourceClass();
        List<Role> classRoles = extractRoles(resourceClass);

        // Get the resource method which matches with the requested URL
        // Extract the roles declared by it
        Method resourceMethod = resourceInfo.getResourceMethod();
        List<Role> methodRoles = extractRoles(resourceMethod);

        try {

            // Check if the user is allowed to execute the method
            // The method annotations override the class annotations
            if (methodRoles.isEmpty()) {
                checkPermissions(classRoles);
            } else {
                checkPermissions(methodRoles);
            }

        } catch (Exception e) {
            requestContext.abortWith(
                Response.status(Response.Status.FORBIDDEN).build());
        }
    }

    // Extract the roles from the annotated element
    private List<Role> extractRoles(AnnotatedElement annotatedElement) {
        if (annotatedElement == null) {
            return new ArrayList<Role>();
        } else {
            Secured secured = annotatedElement.getAnnotation(Secured.class);
            if (secured == null) {
                return new ArrayList<Role>();
            } else {
                Role[] allowedRoles = secured.value();
                return Arrays.asList(allowedRoles);
            }
        }
    }

    private void checkPermissions(List<Role> allowedRoles) throws Exception {
        // Check if the user contains one of the allowed roles
        // Throw an Exception if the user has not permission to execute the method
    }
}

ユーザーに操作を実行する権限がない場合、リクエストは403(禁止)で中止されます。

リクエストを実行しているユーザーを知るには、以前の回答を参照しください。目的の方法に応じて、SecurityContext(で既に設定されているはずです)から取得するContainerRequestContextか、CDIを使用して注入できます。

@Securedアノテーションに役割が宣言されていない場合は、ユーザーが持つ役割に関係なく、認証されたすべてのユーザーがそのエンドポイントにアクセスできると想定できます。

JSR-250アノテーションによる役割ベースの許可のサポート

@Secured上記のようにアノテーションでロールを定義する代わりに@RolesAllowed@PermitAllやなどのJSR-250アノテーションを検討することもできます@DenyAll

JAX-RSは、そのような注釈をそのままではサポートしていませんが、フィルターを使用して実現できます。これらすべてをサポートする場合は、次の点に注意してください。

したがって、JSR-250アノテーションをチェックする承認フィルターは次のようになります。

@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        Method method = resourceInfo.getResourceMethod();

        // @DenyAll on the method takes precedence over @RolesAllowed and @PermitAll
        if (method.isAnnotationPresent(DenyAll.class)) {
            refuseRequest();
        }

        // @RolesAllowed on the method takes precedence over @PermitAll
        RolesAllowed rolesAllowed = method.getAnnotation(RolesAllowed.class);
        if (rolesAllowed != null) {
            performAuthorization(rolesAllowed.value(), requestContext);
            return;
        }

        // @PermitAll on the method takes precedence over @RolesAllowed on the class
        if (method.isAnnotationPresent(PermitAll.class)) {
            // Do nothing
            return;
        }

        // @DenyAll can't be attached to classes

        // @RolesAllowed on the class takes precedence over @PermitAll on the class
        rolesAllowed = 
            resourceInfo.getResourceClass().getAnnotation(RolesAllowed.class);
        if (rolesAllowed != null) {
            performAuthorization(rolesAllowed.value(), requestContext);
        }

        // @PermitAll on the class
        if (resourceInfo.getResourceClass().isAnnotationPresent(PermitAll.class)) {
            // Do nothing
            return;
        }

        // Authentication is required for non-annotated methods
        if (!isAuthenticated(requestContext)) {
            refuseRequest();
        }
    }

    /**
     * Perform authorization based on roles.
     *
     * @param rolesAllowed
     * @param requestContext
     */
    private void performAuthorization(String[] rolesAllowed, 
                                      ContainerRequestContext requestContext) {

        if (rolesAllowed.length > 0 && !isAuthenticated(requestContext)) {
            refuseRequest();
        }

        for (final String role : rolesAllowed) {
            if (requestContext.getSecurityContext().isUserInRole(role)) {
                return;
            }
        }

        refuseRequest();
    }

    /**
     * Check if the user is authenticated.
     *
     * @param requestContext
     * @return
     */
    private boolean isAuthenticated(final ContainerRequestContext requestContext) {
        // Return true if the user is authenticated or false otherwise
        // An implementation could be like:
        // return requestContext.getSecurityContext().getUserPrincipal() != null;
    }

    /**
     * Refuse the request.
     */
    private void refuseRequest() {
        throw new AccessDeniedException(
            "You don't have permissions to perform this action.");
    }
}

注:上記の実装はJerseyに基づいていRolesAllowedDynamicFeatureます。Jerseyを使用する場合は、独自のフィルターを作成する必要はなく、既存の実装を使用するだけです。


このエレガントなソリューションを利用できるgithubリポジトリはありますか?
ダニエルフェレイラカストロ

6
@DanielFerreiraCastroもちろんです。見ていこちらを
cassiomolin

リクエストが承認されたユーザーからのものであり、そのユーザーがデータを「所有」しているためにそのユーザーがデータを変更できることを検証する適切な方法はありますか(たとえば、ハッカーはトークンを使用して別のユーザーの名前を変更できません)?user_id== token.userIdなどのすべてのエンドポイントを確認できることはわかっていますが、これは非常に反復的です。
mFeinstein

@mFeinsteinその答えは、確かにコメントにここで入力できるよりも多くの文字が必要になるでしょう。何らかの指示を与えるために、行レベルのセキュリティを探すことができます。
cassiomolin

行レベルのセキュリティを検索すると、データベースに関する多くのトピックを見ることができます。これを新しい質問として開きます
mFeinstein
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.