首页 > 解决方案 > 如何使用 JWT 令牌授权用户对 asp net core web api 做出反应。何时使用自动化标头不记名令牌?

问题描述

我正在使用 Asp net core Web Api 构建 React 应用程序。我实现了 JWT 授权,我将 jwt 令牌存储在本地存储中。我是否需要将其与授权标头“Bearer”的每个请求一起发送到我的 webapi?我是否需要每次在后端检查标头中的令牌?我希望我的用户在提出请求时获得授权,但我不知道该怎么做。

这是我的 JwtService

public class JWTAuthService
    {
        private readonly JwtTokenConfig jwtTokenConfig;
        private readonly ILogger<JWTAuthService> logger;

        public JWTAuthService(
            JwtTokenConfig jwtTokenConfig,
            ILogger<JWTAuthService> logger)
        {
            this.jwtTokenConfig = jwtTokenConfig;
            this.logger = logger;
        }

        public string BuildToken(Claim[] claims)
        {
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this.jwtTokenConfig.Secret));

            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                    issuer: this.jwtTokenConfig.Issuer,
                    audience: this.jwtTokenConfig.Audience,
                    notBefore: DateTime.Now,
                    claims: claims,
                    expires: DateTime.Now.AddMinutes(this.jwtTokenConfig.AccessTokenExpiration),
                    signingCredentials: creds);

            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        public string BuildRefreshToken()
        {
            var randomNumber = new byte[32];
            using var randomNumberGenerator = RandomNumberGenerator.Create();
            randomNumberGenerator.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }

        public ClaimsPrincipal GetPrincipalFromToken(string token)
        {
            JwtSecurityTokenHandler tokenValidator = new();
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this.jwtTokenConfig.Secret));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var parameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = key,
                ValidateLifetime = false,
            };

            try
            {
                var principal = tokenValidator.ValidateToken(token, parameters, out var securityToken);

                if (!(securityToken is JwtSecurityToken jwtSecurityToken) || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
                {
                    this.logger.LogError($"Token validation failed");
                    return null;
                }

                return principal;
            }
            catch (Exception e)
            {
                this.logger.LogError($"Token validation failed: {e.Message}");
                return null;
            }
        }
    }
}

这是我返回用户 ID 的身份验证

 [HttpPost("user")]
        public string UserAuth([FromBody] string accessToken)
        {
            
            ClaimsPrincipal claimsPrincipal = this.jwtAuthService.GetPrincipalFromToken(accessToken);
            string id = claimsPrincipal.Claims.First(c => c.Type == "id").Value;

           var userId = JsonConvert.SerializeObject(id);
            return userId;

        }

这是我在反应中的登录组件

const Login = () => {
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');
    const [redirect, setRedirect] = useState(false);

    const submit = async (e) => {
        e.preventDefault();

        const user = {
            username,
            password,
        };
        
        await fetch('https://localhost:44366/api/AppUsers/login', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            credentials: 'include',
            body: JSON.stringify(user),
        }).then(response => response.json()).then(res => {

            if (res.AccessToken) {
                localStorage.setItem("jwt", res.AccessToken);
            }
        })

        setRedirect(true);
    }

    if (redirect) {
        return <Redirect to="/" />
    }

here is return state

我的主页上有帖子,用户可以在其中提交评论。我需要 userId 向我的 Api 发出发布请求。首先,我请求获取 userId,然后发布评论。我知道那不是方法,我需要一些帮助来解决这个问题。

const Card = (props) => {
  const [text, setText] = useState("");
  const [showMore, setShowMore] = useState(false);
  const [userId, setUserId] = useState();

  const {
    postId,
    key,
    profilePicture,
    image,
    comments,
    likedByText,
    likedByNumber,
    hours,
    content,
    title,
    accountName,
  } = props;

  const submitComment = (e, postId) => {
    e.preventDefault();

    const jwt = localStorage.getItem("jwt");
    const fetchUrl = `https://localhost:44366/api/AppUsers/user`;

    const fetchData = () => {
      fetch((fetchUrl),
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(jwt),
        })
        .then((res) => res.json())
        .then((result) => setUserId(result))
        .catch((err) => {
          console.log(err);
        });
    };

    fetchData();

    const id = postId;
    const data = {
      Content: text,
      UserId: userId,
      PostId: id ,
    }

    fetch('https://localhost:44366/api/Comments/create', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        
      },
      body: JSON.stringify(data)
    })
      .catch((error) => {
        console.error('Error:', error);
      });

    setText('');
  }
  return (
    <div className="card" key={id}>
      <header>
        <Profile iconSize="big" image={profilePicture} accountName={accountName} />
      </header>
      <p className='text-center'>{title}</p>
      <p className='text-center'>  {content}</p>
      <ImageSlider slides={image} />

      <CardMenu />
      <div className="likedBy">
        <Profile iconSize="small" image={profilePicture} />
        <span>
          Liked by <strong>{likedByText}</strong> and{" "}
          <strong>{comments.Lenght} 50 others</strong>
        </span>
      </div>
      <div className="comments">
        {comments?.slice(0, 5).map((comment) => (

          <Comment
            key={comment.id}
            accountName={comment.User.FirstName ? comment.User.FirstName : null}
            comment={comment.Content}
          />
        )
        )}

        {showMore && comments?.slice(5).map((comment) => (
          <Comment
            key={comment.id}
            accountName={comment.User.FirstName ? comment.User.FirstName : null}
            comment={comment.Content}
          />
        ))}

        <button type="button" className="button" onClick={() => setShowMore(true)}>Show more comments</button>
      </div>
      
      <div className="timePosted">Before {hours} hours.</div>
      <form data={postId} onSubmit={e => submitComment(e, id)}>
        <div className="addComment">
          <textarea type="text" value={text} placeholder="Напишете коментар" className="commentText" onChange={(e) => setText(e.target.value)} />
          <button className="btn btn-primary" type="submit"  >Post</button>
        </div>
      </form>
    </div>
  );
}

export default Card;

标签: c#reactjs.netasp.net-corereact-hooks

解决方案


是的,您需要添加授权标头“Bearer”,您还可以自己创建一个自定义标头并将令牌发送到那里。

现在在后端你放了一个中间件,在你到达之前检查令牌。

在身份验证中,我个人使用 axios,您可以在那里配置一个自动插入标题的 api


推荐阅读