Table of Contents
-
- 1 login verification
- 2 Conversational Technology
-
- 2.1 Introduction to Conversation Technology
- 2.2 Cookies
- 2.2 Session
- 2.4 Token Technology
- 2.5 JWT Tokens
-
- 2.5.1 Introduction
- 2.5.2 Generation and verification
- 2.5.3 Login and issue token
- 3 Unified interception technology
-
- 3.1 Filter Filter
-
- 3.1.1 Filter usage steps
- 3.1.2 Code implementation
- 3.1.3 Details
- 3.2 Interceptor Interceptor
-
- 3.2.1 Steps to use the interceptor
- 3.2.2 Code implementation
- 3.2.3 Details
- 4 Implementation steps of the login function
- 5 exception handling
-
- 5.1 Abnormal solution
- 5.2 Global exception handler
1 Login verification
The so-called login verification means that after we receive the request from the browser on the server side, we first need to verify the request. First, check whether the user is logged in. If the user is already logged in, just execute the corresponding business operation directly; As a result, it finally jumps to the login page, requiring him to access the corresponding data after a successful login.
To complete the above operations, we will involve two technologies in web development:
- conversational technology
- Unified interception technology
There are also two realistic solutions for unified interception technology:
- Filter filter in Servlet specification
- The interceptor interceptor provided by Spring
2 Conversational techniques
2.1 Introduction to conversational technology
- Session: In web development, a session refers to a connection between the browser and the server, which we call a session.
It should be noted that the session is associated with the browser. When three browser clients establish connections with the server, there will be three sessions. The same browser has requested the server multiple times before closing, and these multiple requests belong to the same session.
- Session Tracking: A method of maintaining browser state. The server needs to identify whether multiple requests come from the same browser in order to share data between multiple requests in the same session.
Using session tracking technology is to share data between multiple requests in the same session.
Reasons for sharing data:
Since HTTP is a stateless protocol, in order to obtain the data generated by the previous request in subsequent requests, it is necessary to share data between multiple requests in one session
There are two types of session tracking technologies:
- Cookies (client session tracking technology)
- Session (server-side session tracking technology)
- token technology
2.2 Cookies
Cookie is a client session tracking technology, which is stored in the client browser
- Response header Set-Cookie: Set Cookie data
- Request header Cookie: carrying Cookie data
Pros and Cons
- Advantages: technologies supported in the HTTP protocol
- shortcoming:
- Cookies cannot be used in mobile APP (Android, IOS)
- Not secure, users can disable cookies by themselves
- Cookies cannot cross domains
Distinguish dimensions across domains:
As long as any of the following three dimensions are different, it is a cross-domain operation
- protocol
- IP/protocol
- port
2.2 Session
Session, which is a server-side session tracking technology, so it is stored on the server side
-
Get Session
-
Response Cookie (JSESSIONID)
-
Find Session
Pros and Cons
- Advantages: Session is stored on the server side, safe
- shortcoming:
- Session cannot be used directly in a server cluster environment
- All the Disadvantages of Cookies
2.4 Token technology
Token, in fact, it is an identification of a user identity, and its essence is a string.
Pros and Cons
- advantage:
- Support PC and mobile
- Solve the authentication problem in the cluster environment
- Reduce the storage pressure on the server (no need to store on the server side)
- Disadvantages: You need to implement it yourself (including token generation, token transfer, and token verification)
2.5 JWT Token
Earlier we introduced the implementation of session tracking based on token technology. The token mentioned here is the identity of the user, and its essence is a string. Tokens come in many forms, we are using the powerful JWT token.
2.5.1 Introduction
Full name of JWT: JSON Web Token
-
Defines a concise, self-contained format for securely transmitting information in JSON data format between communicating parties. This information is reliable due to the presence of digital signatures.
Concise: It means that jwt is a simple string. It can be passed directly in the request parameter or request header.
Self-contained: refers to the jwt token, which seems to be a random string, but we can store custom data content in the jwt token according to our own needs. For example: the relevant information of the user can be directly stored in the jwt token.
The composition of JWT: (JWT token consists of three parts, and the three parts are separated by English dots)
-
The first part: Header (header), record token type, signature algorithm, etc.
-
The second part: Payload (payload), carrying some custom information, default information, etc.
-
The third part: Signature (signature), to prevent Token from being tampered with and to ensure security. Add the header, payload, and the specified secret key, and calculate it through the specified signature algorithm.
JWT converts the original JSON format data into a string through base64 encoding?
Base64 encoding: It is an encoding method based on 64 printable characters to represent binary data.
2.5.2 Generate and verify
Generation of JWT tokens.
First introduce the JWT dependency:
<!-- JWT dependency --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
After introducing the JWT token, you can call the API provided in the toolkit to complete the generation and verification of the JWT token
Tool class: Jwts
Generate JWT code implementation:
@Test public void genJwt(){<!-- --> Map<String,Object> claims = new HashMap<>(); claims.put("id",1); claims.put("username","Tom"); String jwt = Jwts. builder() .setClaims(claims) //custom content (payload) .signWith(SignatureAlgorithm.HS256, "iheima") //signature algorithm .setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //validity period .compact(); System.out.println(jwt); }
Verify the JWT token (parse the generated token):
@Test public void parseJwt(){<!-- --> Claims claims = Jwts. parser() .setSigningKey("iheima")//Specify the signing key (must ensure that the same signing key is used when generating the token) .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk") .getBody(); System.out.println(claims); }
Things to note when using JWT tokens:
- The signature key used for JWT verification must match the key used for generating JWT tokens.
- If an error is reported during the JWT token parsing and verification, it means that the JWT token has been tampered with or expired, and the token is illegal.
2.5.3 Login to issue token
Steps to implement:
- Introduce JWT tool class
- Create the com.iheima.utils package under the project project, and copy the provided JWT tool class to the package
- After the login is complete, call the tool class to generate a JWT token and return
JWT tool class
public class JwtUtils {<!-- --> private static String signKey = "iheima";//signature key private static Long expire = 43200000L; //valid time /** * Generate JWT token * @param claims The content stored in the payload of the second part of the JWT load * @return */ public static String generateJwt(Map<String, Object> claims){<!-- --> String jwt = Jwts. builder() .addClaims(claims)//custom information (payload) .signWith(SignatureAlgorithm.HS256, signKey)//signature algorithm (header) .setExpiration(new Date(System.currentTimeMillis() + expire))//expiration time .compact(); return jwt; } /** * Parse the JWT token * @param jwt JWT token * @return The content stored in the second part of JWT load payload */ public static Claims parseJWT(String jwt){<!-- --> Claims claims = Jwts. parser() .setSigningKey(signKey)//Specify the signature key .parseClaimsJws(jwt)//Specify token Token .getBody(); return claims; } }
Successful login, generate JWT token and return
@RestController @Slf4j public class LoginController {<!-- --> //Depend on the business layer object @Autowired private EmpService empService; @PostMapping("/login") public Result login(@RequestBody Emp emp) {<!-- --> //Call the business layer: login function Emp loginEmp = empService. login(emp); //judgment: whether the logged-in user exists if(loginEmp !=null ){<!-- --> //custom information Map<String, Object> claims = new HashMap<>(); claims.put("id", loginEmp.getId()); claims.put("username",loginEmp.getUsername()); claims.put("name",loginEmp.getName()); //Use the JWT tool class to generate an identity token String token = JwtUtils.generateJwt(claims); return Result. success(token); } return Result.error("Username or password error"); } }
3 Unified interception technology
Two solutions to uniformly intercept all requests to verify the validity of tokens:
- Filter
- Interceptor interceptor
3.1 Filter
What is Filter
- Filter represents a filter, which is one of the three major components of JavaWeb (Servlet, Filter, Listener).
3.1.1 Steps to use the filter
- Step 1, define the filter: 1. Define a class, implement the Filter interface, and rewrite all its methods.
- Step 2, configure the filter: Add the @WebFilter annotation to the Filter class to configure the path to intercept resources. Add @ServletComponentScan to the boot class to enable Servlet component support.
3.1.2 Code implementation
@WebFilter(urlPatterns = "/*") //Configure the request path to be intercepted by the filter ( /* means to intercept all browser requests) public class DemoFilter implements Filter {<!-- --> @Override //Initialization method, only called once public void init(FilterConfig filterConfig) throws ServletException {<!-- --> System.out.println("init initialization method executed"); } @Override //Call after intercepting the request, call multiple times public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {<!-- --> System.out.println("Demo intercepted the request...logic before release"); // release chain. doFilter(request, response); } @Override //Destroy method, only called once public void destroy() {<!-- --> System.out.println("destroy destruction method executed"); } }
After we add the @WebFilter annotation on the Filter class, we need to add an annotation @ServletComponentScan on the startup class, and use this @ServletComponentScan annotation to enable the SpringBoot project to support Servlet components.
@ServletComponentScan @SpringBootApplication public class TliasWebManagementApplication {<!-- --> public static void main(String[] args) {<!-- --> SpringApplication.run(TliasWebManagementApplication.class, args); } }
3.1.3 Details
We have completed the quick start procedure of the Filter filter, and then we will introduce some details of the filter in use in detail. Mainly introduce the details of the following three aspects:
- The execution flow of the filter
init -> release before doFilter -> release -> after doFilter release -> destroy - Filter interception path configuration
Intercept path | urlPatterns value | meaning |
---|---|---|
Intercept specific path | /login | Only when accessing the /login path, will it be intercepted |
Directory intercept | /emps/* | All resources accessed under /emps will be intercepted |
Intercept all | /* | Access to all resources will be blocked |
- filter chain
init1 -> release before doFilter1 -> release 1 -> init2 -> release before doFilter2 -> release 2 -> after doFilter2 release -> destroy2 -> after doFilter1 release -> destroy1
The execution priority is determined by the automatic sorting of the filter class names, the higher the class name, the higher the priority.
3.2 Interceptor Interceptor
Interceptors are provided in the Spring framework to dynamically intercept the execution of controller methods.
3.2.1 Interceptor usage steps
- define interceptor
- Register configuration interceptor
3.2.2 Code implementation
**Custom interceptor:** Implement the HandlerInterceptor interface and rewrite all its methods
//custom interceptor @Component public class LoginCheckInterceptor implements HandlerInterceptor {<!-- --> //Execute before the target resource method is executed. Return true: let go Return false: don't let go @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {<!-- --> System.out.println("preHandle .... "); return true; //true means release } // Execute after the target resource method is executed @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {<!-- --> System.out.println("postHandle ... "); } // Execute after the view is rendered, and finally execute @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {<!-- --> System.out.println("afterCompletion...."); } }
Register Configuration Interceptor: Implement the WebMvcConfigurer interface and rewrite the addInterceptors method
@Configuration public class WebConfig implements WebMvcConfigurer {<!-- --> //custom interceptor object @Autowired private LoginCheckInterceptor loginCheckInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) {<!-- --> //Register custom interceptor object registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//Set the request path intercepted by the interceptor ( /** means to intercept all requests) } }
3.2.3 Details
- Interceptor interception path configuration
Intercept Path | Meaning | Example |
---|---|---|
/* | Level one path | can match /depts, /emps, /login, but cannot match /depts/1 |
/** | Any-level path | can match /depts, /depts/1, /depts/1/2 |
/depts/* | The first-level path under /depts | can match /depts/1, but cannot match /depts/1/2, /depts |
/depts/** | Any level path under /depts | can match /depts, /depts/1, /depts/1/2 , cannot match /emps/1 |
- The execution flow of the interceptor
DispatcherServlet -> preHandle -> postHandle -> afterCompletion -> DispatcherServlet
Difference between filter and interceptor:
- The interface specifications are different: filters need to implement the Filter interface, while interceptors need to implement the HandlerInterceptor interface.
- The scope of interception is different: Filter will intercept all resources, while Interceptor will only intercept resources in the Spring environment.
4 Implementation steps of login function
- get request url
- Determine whether login is included in the request url, if it is included, it means login operation, let it go
- Get the token in the request header (token)
- Determine whether the token exists, if not, return an error result (not logged in)
- Parse the token, if the parsing fails, return an error result (not logged in)
- let go
5 exception handling
5.1 Abnormal solution
- Solution 1: Perform try…catch processing in all methods of all Controllers
- Disadvantage: bloated code (not recommended)
- Solution 2: Global exception handler
- Pros: Simple, elegant (recommended)
5.2 Global exception handler
Define a global exception handler
- Define a class and add an annotation @RestControllerAdvice to the class
- Define a method to catch exceptions, which need to be annotated @ExceptionHandler. Use the value attribute in the @ExceptionHandler annotation to specify which type of exception we want to capture.
@RestControllerAdvice public class GlobalExceptionHandler {<!-- --> //handle the exception @ExceptionHandler(Exception.class) //Specify the type of exception that can be handled public Result ex(Exception e){<!-- --> e.printStackTrace();//Print the exception information in the stack // After catching an exception, respond with a standard Result return Result.error("Sorry, the operation failed, please contact the administrator"); } }
@RestControllerAdvice = @ControllerAdvice + @ResponseBody
The return value of the exception handling method will be converted to json and then responded to the front end