springboot + jwt implement token authentication

JWT's official website: https://jwt.io/
JWT (the Java version) of github Address: https://github.com/jwtk/jjwt

What is the JWT

Web token JSON (the JWT) , is based on the statement to transfer between the network environment and the execution of JSONopen standards ((RFC 7519). Definition of a compact, self-contained method for communication between parties to JSONthe object transmission of information in the form of security. because there is a digital signature, the information is credible, the JWT can use the HMACalgorithm or the RSApublic and private keys to sign.

JWT request process

image.png

1. The user account and the surface of the post request issued;
2. server using a private key to create a jwt;
3. jwt the server returns to the browser;
4. The browser sends a request string jwt image server in the request header;
5. the authentication server JWT;
6. the resource response returned to the browser.

JWT's main scenario

Authentication In this scenario, once the user has completed the landing, JWT included in each of the next request, it can be used to authenticate users as well as access to routes, services and resources for verification. Because of its overhead is very small, you can easily transfer system different domain names, all currently in the single sign-on (SSO) in more extensive use of the technology. Exchange of information between the parties to communicate using JWT data coding is a very safe way, because it is a signed message, the sender can ensure that information is not transmitted through forged.

advantage

1. Concise (Compact): can URL, POSTparameters or HTTP headertransmission, because of the small amount of data, transmission speed is fast
2. The self-contained (Self-contained): it contains information about all the load required by the user, avoiding multiple query the database
3. because Tokenis JSONencrypted form stored in the client, it JWTis cross-language, any web form in principle support.
4. no need to store session information on the server side, particularly for distributed micro service.

`

JWT structure

JWT is composed of three pieces of information, these three pieces of information with a text link together constitute JWT string.
like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

JWT consists of three components:
Header header (header contains metadata token, and contains the type signature and / or encryption algorithms)
Payload load (like article carried on an airplane)
the Signature Signature / visa

Header

JWT head carries two pieces of information: token and type of encryption algorithm.

{ 
  "alg": "HS256",
   "typ": "JWT"
} 

Statement type: Here is jwt
assertion of the encryption algorithm: usually used as HMAC SHA256

Encryption algorithm is a one-way hash function algorithm, a common MD5, SHA, HAMC.
The MD5 (Message-Digest algorithm. 5) (Information - digest algorithm) abbreviations widely used in encryption and decryption techniques commonly used in the file verification. check? No matter how big the file, after MD5 can generate a unique MD5 value of
SHA (Secure Hash Algorithm, Secure Hash Algorithm) , digital signatures and other cryptographic applications important tool in safe MD5
HMAC (Hash the Message Authentication Code) , hash message authentication code, the authentication protocol hash algorithm key. A fixed-length value generated using the public key as a function of authentication and identification, with this identity authentication message integrity. Commonly used in the interface signature verification

Payload

Load local storage is valid information.
Effective information consists of three parts
STATEMENT 1. standard registered
2. Public Statement
3. Statement private

Standard registration statement (recommended, but not mandatory to use):

iss: Jwt Issuer
sub: user-facing (facing the user jwt)
aud: one receiving jwt of
exp: expiration timestamps (jwt expiration time, the expiration time must be greater than the time of issue)
nbf: Definitions Before what time, which are jwt not available.
iat: jwt the issue of time
jti: jwt unique identity, is mainly used as a one-off token, so avoid replay attacks.

Public statement:

Public declarations can add any information, general information about the user to add the necessary information or other business needs, but is not recommended for sensitive information to add, in part because the client can decrypt.

Private statement:

Private statement is a statement providers and consumers as common definition, is generally not recommended to store sensitive information, as base64is decrypted symmetric, meaning that some of the information may be classified as plaintext.

Signature

The third part is a jwt visa information, the visa information consists of three parts:
header (after Base64)
payload (after Base64)
Secret

This section requires base64encrypted headerand base64encrypted payloadusing the .strings are connected, then headerencrypted manner declared salt secretcomposition encryption, and constitutes jwta third portion.
The key secretis stored on the server, the server will be generated from this key tokenand validate, so they need to protect.

Let's integrate SpringBoot and the JWT

Introducing JWTdependence, because it is based Java, it is necessary thatjava-jwt

<dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>3.4.0</version>
</dependency>

You need to customize two notes

Used to skip verificationPassToken

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}

Login required to operate commentUserLoginToken

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
    boolean required() default true;
}

@Target: The role of the target annotation

@Target(ElementType.TYPE)- interfaces, classes, enumerations, annotations
@Target(ElementType.FIELD)- field, enumeration constants
@Target(ElementType.METHOD)- Method
@Target(ElementType.PARAMETER)- Method Parameter
@Target(ElementType.CONSTRUCTOR)- Constructor
@Target(ElementType.LOCAL_VARIABLE)- Local variables
@Target(ElementType.ANNOTATION_TYPE)- annotations
@Target(ElementType.PACKAGE)- package

@Retention: Annotation reserved position

RetentionPolicy.SOURCE: This type of Annotationsthe source code level only retained, it will be ignored by the compiler when the classnot contain bytecode file.
RetentionPolicy.CLASS: This type Annotationsis reserved by the compiler, the default retention policy, in classthe presence of the file, but JVMwill be ignored, can not get running.
RetentionPolicy.RUNTIME: This type Annotationswill be JVMretained, so that they can be run in JVMthe read and use or other uses reflection code.

@Document: Indicates that the annotation will be included in javadocthe
@Inherited: description subclasses inherit the parent class annotation

A simple custom entity class User, using the lombokwrite simplified entity class

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    String Id;
    String username;
    String password;
}

You need to write tokenthe Generation

public String getToken(User user) {
        String token="";
        token= JWT.create().withAudience(user.getId())
                .sign(Algorithm.HMAC256(user.getPassword()));
        return token;
    }

Algorithm.HMAC256(): Use HS256generated tokenkey is the user's password, unique key, then can be saved on the server.
withAudience()Deposit needs to be saved in tokenthe information, here I put the user IDinto tokenthe

Next you need to write an interceptor to obtain tokenand verifytoken

public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    UserService userService;
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if(!(object instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod=(HandlerMethod)object;
        Method method=handlerMethod.getMethod();
        //检查是否有passtoken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new RuntimeException("无token,请重新登录");
                }
                // 获取 token 中的 user id
                String userId;
                try {
                    userId = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                User user = userService.findUserById(userId);
                if (user == null) {
                    throw new RuntimeException("用户不存在,请重新登录");
                }
                // 验证 token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("401");
                }
                return true;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, 
                                  HttpServletResponse httpServletResponse, 
                            Object o, ModelAndView modelAndView) throws Exception {

    }
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, 
                                          HttpServletResponse httpServletResponse, 
                                          Object o, Exception e) throws Exception {
    }

Implement a interceptor need to implement HandlerInterceptorthe interface

HandlerInterceptorMain interface defines three methods
1 boolean preHandle ():
Pretreatment callback method, to achieve the pre-processor, in response to the third parameter processor, custom Controller, the return value truerepresents a continuing process (e.g., a call to the next process or interceptors device) or followed by execution
postHandle()and afterCompletion(); falseflowchart showing an interrupt, will not continue to call other interceptors or processor, interrupt execution.

2 void postHandle():
After processing the callback method to achieve post-processing processor ( DispatcherServletCalled before a return to a view rendering), then we can modelAndViewprocess the data model (model and view objects) or to view processed modelAndViewalso may be null.

3 void afterCompletion():
the whole callback request is processed, the method also requires a current corresponding Interceptorto preHandle()only executes if the return value is true, i.e. the DispatcherServletrendering corresponding view after execution. For resource cleanup. Complete request a callback method is completed. Such as performance monitoring, we can end this record time and outputs time-consuming, it can also clean up some resources, like try-catch-finallyin finally, but only call processor execution chain

The main processes:

1. From the httpRemove request header token,
2. a method for mapping determines whether
3. Check passtokenNotes, there are skip authentication
4. There is no need to check the annotation user login, and verify that there is the need to remove
5. authentication can access, not by the relevant error message will be reported

Configuring interceptors

In the configuration class annotations added @Configuration, marked with a class is configured as a class of that class and SpringBeanadded to IOCthe vessel

@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
    }
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}

WebMvcConfigurerAdapterThe abstract class in fact there is no way to achieve, just empty implements the interface
WebMvcConfigurerall methods within and did not give any business logic processing, which is designed just right so that we do not have to implement those we do not, we have handed over WebMvcConfigurerAdapterabstract space-realization, if we need to make a logical process for a specific method, only need to
WebMvcConfigurerAdaptersubclass @Overridethe corresponding method on it.

NOTE:
In SpringBoot2.0and Spring 5.0of WebMvcConfigurerAdapteralready abandoned
the Internet has changed to inherit said WebMvcConfigurationSupport, but the next test, or expired

Solution:

Direct implementation of WebMvcConfigurer(the official recommended)

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");   
    }
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}

InterceptorRegistryIn addInterceptorneed an implementation of HandlerInterceptorthe interface interceptor instance, addPathPatternsa method is provided for filtering path rules interceptor.
Here I intercept all requests, by determining whether there are @LoginRequiredannotations decide whether you need to log

Join Login operate annotation data access interface

@RestController
@RequestMapping("api")
public class UserApi {
    @Autowired
    UserService userService;
    @Autowired
    TokenService tokenService;
    //登录
    @PostMapping("/login")
    public Object login(@RequestBody User user){
        JSONObject jsonObject=new JSONObject();
        User userForBase=userService.findByUsername(user);
        if(userForBase==null){
            jsonObject.put("message","登录失败,用户不存在");
            return jsonObject;
        }else {
            if (!userForBase.getPassword().equals(user.getPassword())){
                jsonObject.put("message","登录失败,密码错误");
                return jsonObject;
            }else {
                String token = tokenService.getToken(userForBase);
                jsonObject.put("token", token);
                jsonObject.put("user", userForBase);
                return jsonObject;
            }
        }
    }
    @UserLoginToken
    @GetMapping("/getMessage")
    public String getMessage(){
        return "你已通过验证";
    }
}

Without comment, then no default authentication, the interface is generally not verified. In getMessage()I add a comment login, indicates that this interface must be logged obtain tokenafter, plus the request header tokenand can only be accessed by verified

The following test, start the project, using the postman test interface

In no tokenaccess in the case of api/getMessagean interface

image.png


I here use a unified exception handling, so only see an errormessage

 

Below to log in, in order to gaintoken

image.png


Log In verification operation I did not add annotations, so you can directly access

 

The tokenincrease in the request header, again access api/getMessageinterfaces

image.png


Note: This keymust not be wrong, because the interceptor is key to take tokenthe value of
String token = httpServletRequest.getHeader("token");
adding tokenafter which you can pass the authentication and access interface

 

github project Source Address: https://github.com/JinBinPeng/springboot-jwt



Original Address: https://www.jianshu.com/p/e88d3f8151db

Guess you like

Origin blog.csdn.net/xiaomojun/article/details/86634865