Some common authentication schemes include: BasicBearer
The server responds to a client with a 401 (Unauthorized) response status and provides information on how to authorize with a WWW-Authenticate response header containing at least one challenge.
A client that wants to authenticate itself with the server can then do so by including an Authorization request header with the credentials.
Usually a client will present a password prompt to the user and will then issue the request including the correct Authorization header.
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.
This information can be verified and trusted because it is digitally signed.
JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Authorization
Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token.
Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains.
Structure
The output is three Base64-URL strings separated by dots that can be easily passed in HTML and HTTP environments, while being more compact when compared to XML-based standards such as SAML.
1
Header.Payload.Signature
Header
The header typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.
Then, this JSON is Base64Url encoded to form the first part of the JWT.
1 2 3 4
{ "alg":"HS256", "typ":"JWT" }
Payload
Claims are statements about an entity (typically, the user) and additional data.
There are three types of claims: registered, public, and private claims.
But to avoid collisions they should be defined in the IANA JSON Web Token Registry or be defined as a URI that contains a collision resistant namespace.
Do note that for signed tokens this information, though protected against tampering, is readable by anyone. Do not put secret information in the payload or header elements of a JWT unless it is encrypted.
Signature
To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.
The signature is used to verify the message wasn’t changed along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.
In authentication, when the user successfully logs in using their credentials, a JSON Web Token will be returned.
Whenever the user wants to access a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema.
1
Authorization: Bearer <token>
Stateless
This can be, in certain cases, a stateless authorization mechanism.
The server’s protected routes will check for a valid JWT in the Authorization header, and if it’s present, the user will be allowed to access protected resources.
If the JWT contains the necessary data, the need to query the database for certain operations may be reduced, though this may not always be the case.
Note that if you send JWT tokens through HTTP headers, you should try to prevent them from getting too big.
If you are trying to embed too much information in a JWT token, like by including all the user’s permissions, you may need an alternative solution.
CORS
If the token is sent in the Authorization header, Cross-Origin Resource Sharing (CORS) won’t be an issue as it doesn’t use cookies.
The application or client requests authorization to the authorization server.
When the authorization is granted, the authorization server returns an access token to the application.
The application uses the access token to access a protected resource (like an API).
Do note that with signed tokens, all the information contained within the token is exposed to users or other parties, even though they are unable to change it. This means you should not put secret information within the token.
Workflow
Customers sign in by submitting their credentials to the provider.
Upon successful authentication, it generates JWT containing user details and privileges for accessing the services and sets the JWT expired date in payload.
The server signs and encrypts the JWT if necessary and sends it to the client as a response with credentials to the initial request.
Based on the expiration set by the server, the customer/client stores the JWT for a restricted or infinite amount of time.
The client sends this JWT token in the header for all subsequent requests.
The client authenticates the user with this token. So we don’t need the client to send the user name and password to the server during each authentication process, but only once the server sends the client a JWT.
public String generate(UserDetails details) { return Jwts.builder() // Identifier (or, name) of the user this token represents. .setSubject(details.getUsername()) // Date/time when the token was issued. .setIssuedAt(Date.from(Instant.now())) // Date/time at which point the token is no longer valid. .setExpiration(Date.from(Instant.now().plus(Duration.ofDays(1)))) .signWith(SignatureAlgorithm.HS512, secret) .compact(); }
// JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token if (!StringUtils.isEmpty(authorization) && authorization.startsWith("Bearer ")) { token = authorization.substring(7); username = tokenHelper.username(token); }
// Once we get the token validate it. if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetailsuserDetails= userDetailsService.loadUserByUsername(username); // if token is valid configure Spring Security to manually set authentication if (tokenHelper.validate(token, userDetails)) { UsernamePasswordAuthenticationTokenauthenticationToken= newUsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authenticationToken.setDetails(newWebAuthenticationDetailsSource().buildDetails(request)); // After setting the Authentication in the context, we specify // that the current user is authenticated. So it passes the Spring Security Configurations // successfully. SecurityContextHolder.getContext().setAuthentication(authenticationToken); } }
@Autowired publicvoidconfigureGlobal(AuthenticationManagerBuilder auth)throws Exception { // configure AuthenticationManager so that it knows from where to load // user for matching credentials // Use BCryptPasswordEncoder auth.userDetailsService(detailsService).passwordEncoder(passwordEncoder()); }
@Bean public PasswordEncoder passwordEncoder() { returnnewBCryptPasswordEncoder(); }
@Bean @Override public AuthenticationManager authenticationManagerBean()throws Exception { returnsuper.authenticationManagerBean(); }
@Override protectedvoidconfigure(HttpSecurity httpSecurity)throws Exception { httpSecurity // We don't need CSRF for this example .csrf() .disable() // do not authenticate this particular request .authorizeRequests() .antMatchers("/authenticate") .permitAll() .antMatchers(HttpMethod.OPTIONS, "/**") .permitAll() // all other requests need to be authenticated .anyRequest() .authenticated() .and() .exceptionHandling() .authenticationEntryPoint(unauthorizedEntryPoint) .and() // make sure we use stateless session; session won't be used to // store user's state. .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } }
@Override public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException { // bcrypt is a password-hashing function. // https://bcrypt-generator.com/ if ("zhongmingmao".equals(username)) { returnnewUser( "zhongmingmao", "$2a$12$VvnKaknki2o1saULFLmuduvzVAUMuFZ0LUOrchKnK0jHfLZof3/za", Collections.emptyList()); } thrownewUsernameNotFoundException("User not found with username: " + username); } }