JSON Web Token(JWT):我应该使用响应头部还是响应体部分来传输令牌?

huangapple go评论71阅读模式
英文:

JSON Web Token (JWT): should I use response header or body for token transportation?

问题

我正在开发一个RESTful web服务,用于颁发**JSON Web Tokens (JWT)**形式的访问令牌,以便客户端可以在进一步的授权中使用它们。目前,我将令牌存储在响应体中,但通常建议将其添加到Authorization头部。为什么应该选择头部而不是响应体?在使用一种方法而不是另一种方法时,是否有任何主要优势?

@RestController
@RequestMapping(path = "/auth", produces = "application/json")
public class AccessTokenController {

    @Value("${jwt.secret.key.gen}")
    private String secretKey;

    @Value("${jwt.expiration.days}")
    private int expirationDays;

    private final UsersRepository usersRepository;
    private final PasswordEncoder passwordEncoder;

    @Autowired
    public AccessTokenController(UsersRepository usersRepository, PasswordEncoder passwordEncoder) {
        this.usersRepository = usersRepository;
        this.passwordEncoder = passwordEncoder;
    }

    @PostMapping(value = "/access-token", consumes = "application/json")
    ResponseEntity<Object> getAccessToken(@RequestBody Map<String, String> credentials) {

        // #1: check request body for username and password
        String username = credentials.get("username");
        String password = credentials.get("password");
        if (username.isBlank() || password.isBlank())
            throw new IllegalArgumentException();

        // #2: check database for given username and password
        User user = usersRepository.findByUsername(username)
                .filter(u -> passwordEncoder.matches(password, u.getPassword()))
                .orElseThrow(() -> new BadCredentialsException("No such user with given password"));

        // #3: check 'enabled' status for the requested user
        if (!user.isEnabled())
            throw new DisabledException("User is currently disabled");

        // # 4: create and return access-token (finally!)
        String token = Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + expirationDays * 24 * 60 * 60 * 1000))
                .signWith(SignatureAlgorithm.HS512, secretKey.getBytes())
                .compact();
        return new ResponseEntity<>(Collections.singletonMap("access-token", token),
                HttpStatus.OK);
    }

    @ExceptionHandler({JsonParseException.class, NullPointerException.class, IllegalArgumentException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, String> handleWrongOrMissingCredentials() {
        return Collections.singletonMap("error", "Wrong or missing credentials");
    }

    @ExceptionHandler(AuthenticationException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Map<String, String> handleAuthenticationException(AuthenticationException ex) {
        return Collections.singletonMap("error", ex.getMessage());
    }

}
英文:

I'm developing a RESTful web service that issues access tokens in the form of JSON Web Tokens (JWT), so that clients can use them for further authorization. Currently, I store tokens in a response body, but typically it's advised to add it to Authorization header. Why should I choose header and not response body? Is there any principal advantage on using one method over the other?

@RestController
@RequestMapping(path = &quot;/auth&quot;, produces = &quot;application/json&quot;)
public class AccessTokenController {

    @Value(&quot;${jwt.secret.key.gen}&quot;)
    private String secretKey;

    @Value(&quot;${jwt.expiration.days}&quot;)
    private int expirationDays;

    private final UsersRepository usersRepository;
    private final PasswordEncoder passwordEncoder;

    @Autowired
    public AccessTokenController(UsersRepository usersRepository, PasswordEncoder passwordEncoder) {
        this.usersRepository = usersRepository;
        this.passwordEncoder = passwordEncoder;
    }

    @PostMapping(value = &quot;/access-token&quot;, consumes = &quot;application/json&quot;)
    ResponseEntity&lt;Object&gt; getAccessToken(@RequestBody Map&lt;String, String&gt; credentials) {

        // #1: check request body for username and password
        String username = credentials.get(&quot;username&quot;);
        String password = credentials.get(&quot;password&quot;);
        if (username.isBlank() || password.isBlank())
            throw new IllegalArgumentException();

        // #2: check database for given username and password
        User user = usersRepository.findByUsername(username)
                .filter(u -&gt; passwordEncoder.matches(password, u.getPassword()))
                .orElseThrow(() -&gt; new BadCredentialsException(&quot;No such user with given password&quot;));

        // #3: check &#39;enabled&#39; status for the requested user
        if (!user.isEnabled())
            throw new DisabledException(&quot;User is currently disabled&quot;);

        // # 4: create and return access-token (finally!)
        String token = Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + expirationDays * 24 * 60 * 60 * 1000))
                .signWith(SignatureAlgorithm.HS512, secretKey.getBytes())
                .compact();
        return new ResponseEntity&lt;&gt;(Collections.singletonMap(&quot;access-token&quot;, token),
                HttpStatus.OK);
    }

    @ExceptionHandler({JsonParseException.class, NullPointerException.class, IllegalArgumentException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map&lt;String, String&gt; handleWrongOrMissingCredentials() {
        return Collections.singletonMap(&quot;error&quot;, &quot;Wrong or missing credentials&quot;);
    }

    @ExceptionHandler(AuthenticationException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Map&lt;String, String&gt; handleAuthenticationException(AuthenticationException ex) {
        return Collections.singletonMap(&quot;error&quot;, ex.getMessage());
    }

}

答案1

得分: 3

这是HTTP标准的一部分,所有API都要遵循。简单来说,可以将其视为一组规则,每个人都遵循这些规则,以便在开发服务器或客户端时,可以遵循这些规则以避免不一致。

RFC链接:https://www.rfc-editor.org/rfc/rfc7235#section-4.2

"Authorization"头字段允许用户代理在通常情况下(但不一定是在收到401(未经授权)响应后)与源服务器进行身份验证。其值包含用户代理的身份验证信息,用于请求的资源领域。

Authorization = credentials

现在有许多不同类型的授权头,人们可以使用,一旦使用,就知道可以期望什么。以下是一个简单的列表,展示了一些类型:

  • 基本身份验证(Basic Auth)
  • 令牌身份验证(Bearer Token)
  • API密钥(API Key)
  • 摘要身份验证(Digest Auth)
  • OAuth 2.0
  • Hawk身份验证(Hawk Authentication)
  • AWS签名(AWS Signature)
英文:

It is part of the HTTP standard which all api follow. In basic terms think of a set of rules that everyone follows so that when people develop servers or clients then they can follow this set of rules to avoid discrepancies

RFC https://www.rfc-editor.org/rfc/rfc7235#section-4.2

> The "Authorization" header field allows a user agent to authenticate
> itself with an origin server -- usually, but not necessarily, after
> receiving a 401 (Unauthorized) response. Its value consists of
> credentials containing the authentication information of the user
> agent for the realm of the resource being requested.
>
> Authorization = credentials

There are many different types of authorization headers that people can use right now and once you use it then you know what to expect. Here is a simple list to show a few

       •	Basic Auth
•	Bearer Token
•	API Key
•	Digest Auth
•	Oauth 2.0
•	Hawk Authentication
•	AWS Signature

huangapple
  • 本文由 发表于 2020年9月6日 14:07:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/63761221.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定