2 回答

TA贡献1963条经验 获得超6个赞
基于令牌的身份验证是如何工作的?
客户端向服务器发送他们的凭据(用户名和密码)。 服务器对凭据进行身份验证,如果它们有效,则为用户生成令牌。 服务器将先前生成的令牌与用户标识符和过期日期一起存储在某些存储中。 服务器将生成的令牌发送给客户端。 客户端在每个请求中向服务器发送令牌。 在每个请求中,服务器从传入请求中提取令牌。使用令牌,服务器查找用户详细信息以执行身份验证。 如果令牌有效,服务器将接受请求。 如果令牌无效,服务器将拒绝请求。 一旦执行了身份验证,服务器就会执行授权。 服务器可以提供一个端点来刷新令牌。
你能用JAX-RS2.0做些什么(泽西,RESTEasy和ApacheCXF)
web.xml
使用用户名和密码对用户进行身份验证并发出令牌
@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
application/x-www-form-urlencoded
username=admin&password=123456
public class Credentials implements Serializable { private String username; private String password; // Getters and setters omitted}
@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
Authorization: Bearer <token-goes-here>
@NameBinding
@Secured
@NameBinding@Retention(RUNTIME)@Target({TYPE, METHOD})public @interface Secured { }
ContainerRequestFilter
ContainerRequestContext
@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
保护您的休息端点
@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
.
标识当前用户
重写当前请求的安全上下文
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
SecurityContext
@ContextSecurityContext securityContext;
@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(上下文和依赖项注入)
SecurityContext
@Qualifier@Retention(RUNTIME)@Target({ METHOD, FIELD, PARAMETER })public @interface AuthenticatedUser { }
AuthenticationFilter
Event
@AuthenticatedUser
:
@Inject@AuthenticatedUserEvent<String> userAuthenticatedEvent;
userAuthenticatedEvent.fire(username);
User
.
User
authenticatedUser
@RequestScopedpublic 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
User
User
@Inject@AuthenticatedUserUser authenticatedUser;
@Produces
@Produces
土发委会: 贾克斯-斯普斯卡共和国:
@Produces
AuthenticatedUserProducer
@RequestScoped
SecurityContext
支持基于角色的授权
发行券
不透明的:
显示除值本身以外的其他任何细节(如随机字符串)。 自成一体:
包含有关令牌本身的详细信息(如JWT)。
随机字符串作为标记
Random random = new SecureRandom();String token = new BigInteger(130, random).toString(32);
JWT(JSONWeb令牌)
jti
使用JWT
用JWT处理令牌刷新
exp
refreshLimit
:指示可以刷新令牌多少次。 refreshCount
:指示刷新令牌的次数。
令牌未过期( exp >= now
).刷新令牌的次数少于可以刷新令牌的次数( refreshCount < refreshLimit
).
更新到期日期( exp = now + some-amount-of-time
).增加刷新令牌的次数( refreshCount++
).
refreshLimit
用JWT处理令牌撤销
jti
jti
补充资料
添加回答
举报