1. What is jwt
The full name of
JWT
isJSON Web Token
. If you understand it literally, it feels like a token based on theJSON
format for network transmission. In fact,JWT
is a compactClaims
declaration format, designed for transmission in space-limited environments, common scenarios such asHTTP
Authorization request header parameters andURI
query parameters.JWT
will convertClaims
intoJSON
format, and thisJSON
content will be applied asJWS
structure or the (encrypted) raw string applied to the JWE
structure is passed through the message authentication code (Message Authentication Code
or simplyMAC
) and/or cryptographic operations to digitally sign or integrity protectClaims
.
2. Create backend project
2.1 Add pom dependency
<!-- hutool dependencies --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.16</version> </dependency>
2.2 Edit entity, mapper, service, controller
I won’t write too much here, it’s still the same as the previous article.
2.3 JWT filter class
package com.aaa.filter; import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWTUtil; import com.aaa.until.ResponseMsg; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.List; import java.util.stream.Collectors; @Component public class JWTFilter extends OncePerRequestFilter { /** * Overridden doFilterInternal method * Parse token and verify user information * If the verification is successful, save the user information and release * If verification fails, an error message is returned * If the token is empty and the request path is not in the whitelist, an error message will be returned */ @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // whitelist path String[] whitename = {"/login"}; // Get the token in the request header String token = request.getHeader("token"); // If token is not empty if (StringUtils.isNotBlank(token)) { //Verify whether the token is valid boolean verify = JWTUtil.verify(token, "user".getBytes()); //If verification passes if (verify) { // Parse token and obtain user name and resource information JWT jwt = JWTUtil.parseToken(token); String username = (String) jwt.getPayload("username"); List<String> resources = (List<String>) jwt.getPayload("resources"); //Convert resource information to SimpleGrantedAuthority list List<SimpleGrantedAuthority> resourceList = resources.stream().map(res -> new SimpleGrantedAuthority(res)).collect(Collectors.toList()); //Save user information UsernamePasswordAuthenticationToken userPwdToken = new UsernamePasswordAuthenticationToken(username, null, resourceList); SecurityContextHolder.getContext().setAuthentication(userPwdToken); // Release filterChain.doFilter(request, response); } else { // Verification failed and error message returned ResponseMsg responseMsg = new ResponseMsg(401, "Not logged in", null); printJsonData(response, responseMsg); } } else { // If the token is empty, check whether the request path is in the whitelist String requestURI = request.getRequestURI(); // If in the whitelist, let it go if (ArrayUtils.contains(whitename, requestURI)) { filterChain.doFilter(request, response); } else { //If not in the whitelist, return error message ResponseMsg responseMsg = new ResponseMsg(401, "Not logged in", null); printJsonData(response, responseMsg); } } } public void printJsonData(HttpServletResponse response, ResponseMsg responseMsg) { try { response.setContentType("application/json;charset=utf8"); //Set the response content type to JSON and specify the encoding to UTF-8 ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(responseMsg); // Use ObjectMapper to convert the ResponseMsg object into a JSON string PrintWriter writer = response.getWriter(); writer.print(json); // Write a JSON string to the response output stream writer.flush();//Refresh writer.close();//Close } catch (Exception e) { e.printStackTrace(); // Print exception stack information } } }
2.4 Cross-domain configuration CrossConfig
package com.aaa.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CrossConfig { @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration corsConfiguration = new CorsConfiguration(); //corsConfiguration.setAllowCredentials(true); Allow cookies to be carried corsConfiguration.addAllowedHeader("*"); // Allow all headers corsConfiguration.addAllowedOrigin("*"); // Allow all request origins corsConfiguration.addAllowedMethod("*"); // Desired method get post delete put source.registerCorsConfiguration("/**", corsConfiguration); // All paths are allowed across domains return new CorsFilter(source); } }
2.5 SecurityConfig configuration class
package com.aaa.config; import cn.hutool.jwt.JWTUtil; import com.aaa.filter.JWTFilter; import com.aaa.until.ResponseMsg; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Resource protected UserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Resource public JWTFilter jwtFilter; @Override protected void configure(HttpSecurity http) throws Exception { //.usernameParameter("username")//Login account //.passwordParameter("userpassword");//Login password //.defaultSuccessUrl("/test");//Jump path for successful login //Configure the login page and login processing path http.formLogin().loginPage("/login.html")//The path must be preceded by / .loginProcessingUrl("/login")//Same as the submitted path // Processing logic for successful login .successHandler((request, response, authentication) ->{ Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();//Get resource information List<String> resources = authorities.stream().map(auth -> auth.getAuthority()).collect(Collectors.toList());//Convert to stream to list Map map = new HashMap<>(); map.put("username",authentication.getName()); map.put("resources",resources); //Generate token String token = JWTUtil.createToken(map, "user".getBytes()); ResponseMsg responseMsg = new ResponseMsg(200,"Login successful",token); //Response return information printJsonData(response,responseMsg); }) // Login failure processing logic .failureHandler((request, response, exception)->{ ResponseMsg responseMsg = new ResponseMsg(400,"Verification failed"); printJsonData(response,responseMsg); }); //Configure processing logic that is not allowed by permissions http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> { ResponseMsg responseMsg = new ResponseMsg(403,"Permission not allowed",null); printJsonData(response,responseMsg); }); // Configure paths that do not require verification http.authorizeRequests().antMatchers("/login.html", "login").permitAll();//No verification required //http.authorizeRequests().antMatchers("/test").hasRole("test");//Which permissions are required to access //http.authorizeRequests().antMatchers("/test").hasAnyAuthority("resource");//Which resources must be available to access // Configuring other paths requires verification http.authorizeRequests().anyRequest().authenticated(); //Add JWT filter http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); //Turn off csrf protection http.csrf().disable(); //Configure cross-domain http.cors(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } public void printJsonData(HttpServletResponse response, ResponseMsg responseMsg) { try { response.setContentType("application/json;charset=utf8"); // json format encoding is Chinese ObjectMapper objectMapper = new ObjectMapper(); String s = objectMapper.writeValueAsString(responseMsg);//Use ObjectMapper to convert result from json to string PrintWriter writer = response.getWriter(); writer.print(s); writer.flush(); writer.close(); }catch (Exception e){ e.printStackTrace(); } } }
2.6 SecurityService class
It’s the same as the previous note
3. Create a front-end vue project
login page
Don't forget to add the login page under routing
<template> <div> <el-form style="width: 400px ;margin: auto auto" :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm" > <el-form-item label="name" prop="username"> <el-input type="text" v-model="ruleForm.username" autocomplete="off"></el-input> </el-form-item> <el-form-item label="password" prop="password"> <el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('ruleForm')">Submit</el-button> <el-button @click="resetForm('ruleForm')">Reset</el-button> </el-form-item> </el-form> </div> </template> <script> import qs from 'qs'; export default { data() { return { ruleForm: { username: 'user000',//You have to re-enter the account and password every time you refresh the page, so you write the account and password directly. password: '123456', }, rules: { username: [ {required: true, message: 'Please enter account', trigger: 'blur'} ], password: [ {required: true, message: 'Please enter password', trigger: 'blur'} ] } }; }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { alert('Verification successful, ready to submit') this.$axios.post('/login',qs.stringify(this.ruleForm)).then(res=>{ console.log(res) if (res.data.code == 200){ console.log(res.data.data); //------------------------------------------------ --------------- sessionStorage.setItem("token",res.data.data) adds data as token in sessionstorage //------------------------------------------------ --------------- location.href='/main';//Successfully accessed the main page }else { } }) //location.href='/main' } else { alert('Verification failed'); return false; } }); }, resetForm(formName) { this.$refs[formName].resetFields(); } } } </script>
Add the following code to main.js
Verify whether the token exists, and if so, add the token to the request header.
//Request interceptor instance.interceptors.request.use(config => { if (sessionStorage.getItem("token")){ let token = sessionStorage.getItem("token"); config.headers['token'] = token; } return config; }, error => { return Promise.reject(error); });
4. Summary
Enter the account from the front-end page to send a cross-domain request to the back-end. The back-end accepts the parameters to verify whether they exist in the database. If they exist, check out the user's role permissions and resource permissions in the database and save them in Security. After successful login, use the JWTUtil.createToken() method to generate a token from the user's information and return it to the front-end. The front-end gets the token. The front-end carries the token when it initiates a request to the back-end again. The back-end recognizes it and releases it if it is correct. Incorrect Access failed