catalogue
1, Explain
When the front and back ends are completely separated, the general idea of implementing token verification in Vue project is as follows (too lazy to type. This part quotes the content of the blogger User login and token authentication in Vue project):
1. When logging in for the first time, the front end calls the back end login interface and sends the user name and password
2. The back end receives the request, verifies the user name and password, and returns a token to the front end if the verification is successful
3. The front end gets the token, stores the token in localStorage and vuex, and jumps to the routing page
4. Each time the front end jumps the route, it will judge whether there is a token in the localStroage. If not, it will jump to the login page, and if there is, it will jump to the corresponding route page
5. Every time you call the back-end interface, you should add a token in the request header
6. The back-end judges whether there is a token in the request header. If there is a token, it will get the token and verify the token. If the verification is successful, it will return data. If the verification fails (for example, the token expires), it will return 401. If there is no token in the request header, it will also return 401
7. If the front end gets the status code 401, it clears the token information and jumps to the login page
2, Background (springboot)
1. Add dependent package
Add jwt dependency package in springboot project, which is mainly used to encrypt and decrypt token.
<!--jwt rely on--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
2. Add token tool class
Add token tool class, TokenUtil
import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; import com.lin.springMVC.domain.User; import java.util.Date; public class TokenUtil { private static final long EXPIRE_TIME= 10*60*60*1000; private static final String TOKEN_SECRET="txdy"; //Key salt /** * Signature generation * @param user * @return */ public static String sign(User user){ String token = null; try { Date expiresAt = new Date(System.currentTimeMillis() + EXPIRE_TIME); token = JWT.create() .withIssuer("auth0") .withClaim("userAccount", user.getUserAccount()) .withExpiresAt(expiresAt) // HMAC256 encryption algorithm is used. .sign(Algorithm.HMAC256(TOKEN_SECRET)); } catch (Exception e){ e.printStackTrace(); } return token; } /** * Signature verification * @param token * @return */ public static boolean verify(String token){ try { JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build(); DecodedJWT jwt = verifier.verify(token); System.out.println("Certification passed:"); System.out.println("userAccount: " + jwt.getClaim("userAccount").asString()); System.out.println("Expiration time: " + jwt.getExpiresAt()); return true; } catch (Exception e){ return false; } } }
3. Create interceptor
import com.alibaba.fastjson.JSONObject; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; @Component public class TokenInterceptor implements HandlerInterceptor { private static final String token = "token#"; @Autowired RedisHandler redisHandler; @Value("${api.api_prefix}") public String api_prefix; /** * Call before request processing, and enter this method before entering the Controller method * Return true to continue downward execution, and return false to cancel the current request * Login interception and permission resource control */ @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { String userAccount = null; String redirectUrl = "login"; if ((httpServletRequest.getParameter("language") != null && !httpServletRequest.getParameter("language").equals("en"))) { redirectUrl += "_en"; } Cookie[] cookies = httpServletRequest.getCookies();//Fetch from cookie String token1 = httpServletRequest.getHeader("Access-Token");// Fetch the token from the http request header if(token1 != null){ boolean result = TokenUtil.verify(token1); if(result){ System.out.println("Through interceptor"); return true; } } try{ JSONObject json = new JSONObject(); json.put("msg","token verify fail"); json.put("status","401"); httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("application/json; charset=utf-8"); PrintWriter out = null; out = httpServletResponse.getWriter(); out.append(json.toString()); return false; }catch (Exception e){ e.printStackTrace(); JSONObject json = new JSONObject(); json.put("msg","error"); json.put("status","500"); httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("application/json; charset=utf-8"); PrintWriter out = null; out = httpServletResponse.getWriter(); out.append(json.toString()); return false; } }
4. Entrance interception
Configuration entry interception, ShiroConfiguration class
import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.Filter; /** * shiro Configuration class */ @Configuration public class ShiroConfiguration { /** * Shiro Name of the Web filter Factory: shiroFilter */ @Bean(name = "shiroFilter") //Equivalent to < bean id = "shirofilter" class = "org. Apache. Shiro. Spring. Web. Shirofilterfactorybean" > public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { //for defining the master Shiro Filter ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //Sets the application {@code SecurityManager} instance to be used by the constructed Shiro Filter //This is a required property - failure to set it will throw an initialization exception. shiroFilterFactoryBean.setSecurityManager(securityManager);//<property name="securityManager" ref="securityManager"/> Map<String, Filter> filterMap = new LinkedHashMap<>(); //Most implementations subclass one of the //{@link AccessControlFilter}, {@link AuthenticationFilter}, {@link AuthorizationFilter} classes to simplify things, //and each of these 3 classes has configurable properties that are application-specific. filterMap.put("authc", new AjaxPermissionsAuthorizationFilter());//Add filter shiroFilterFactoryBean.setFilters(filterMap); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();//Equivalent to < property name = "filterchaindefinitions" > filterChainDefinitionMap.put("/", "anon"); // filterChainDefinitionMap.put("/debug/**", "anon"); filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/register", "anon"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } }
5. Configure cross domain
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 CorsConfig { @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); final CorsConfiguration corsConfiguration = new CorsConfiguration(); /*Allow requests with authentication information*/ corsConfiguration.setAllowCredentials(true); /*Allowed client domain name*/ corsConfiguration.addAllowedOrigin("*"); /*Client request header to allow server access*/ corsConfiguration.addAllowedHeader("*"); /*Method name allowed to access, GET POST, etc*/ corsConfiguration.addAllowedMethod("*"); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); return new CorsFilter(urlBasedCorsConfigurationSource); } }
6. Login interface
@PostMapping("/login") @ResponseBody public ModelMap userLogin(@RequestBody User userform, HttpServletRequest request, HttpServletResponse response, @RequestHeader("language") String language) { String status; String url = redirectUrl; String token1=""; int code; ModelMap result = new ModelMap(); Subject currentUser = SecurityUtils.getSubject(); try { currentUser.login(token); User user = userService.getUserInfo(userform.getUserAccount(), ShiroUtils.generatePwdEncrypt(userform.getUserPassword(), salt)); if (user != null) { try { token1 = TokenUtil.sign(userform); status = "success"; code = 200; } catch (JedisConnectionException | RedisConnectionFailureException e) { //User login record userLog(user, request.getRemoteUser(), "login_error:" + e.toString()); status = "fail"; code = 400; } } else { User userTemp = new User(); userTemp.setUserId("null"); userTemp.setUserAccount("null"); userLog(userTemp, request.getRemoteUser(), "login_error:user is not exist!"); status = "fail"; code = 401; } } catch (AuthenticationException e) { //User login record User userTemp = new User(); userTemp.setUserId("null"); userTemp.setUserAccount("null"); userLog(userTemp, request.getRemoteUser(), "login_error:user is not exist!"); status = "fail"; code = 401; } result.put("status", status); result.put("code", code); result.put("token",token1); result.put("url", "null"); return result; }
3, Front end (vue)
1. Create a store folder in the src directory
Create a new index in the store folder js:
[SET_TOKEN]: (state, token: string) => { state.token = token; ls.set(STORAGE_TOKEN_KEY, token); },
2. Add routing guard
import router from '@/router'; import store from '@/store'; import localStorage from '@/utils/local-storage'; import { allowList, loginRoutePath } from '../define-meta'; import { STORAGE_TOKEN_KEY } from '@/store/mutation-type'; // eslint-disable-next-line import { GENERATE_ROUTES, GENERATE_ROUTES_DYNAMIC, GET_INFO } from '@/store/modules/user/actions'; router.beforeEach(async to => { const userToken = localStorage.get(STORAGE_TOKEN_KEY); // token check if (!userToken) { // Whitelist routing list check if (allowList.includes(to.name as string)) { return true; } if (to.fullPath !== loginRoutePath) { // If you are not logged in, go to the login page return { path: loginRoutePath, replace: true, }; } return to; } // check login user.role is null if (store.getters['user/allowRouters'] && store.getters['user/allowRouters'].length > 0) { return true; } else { const info = await store.dispatch(`user/${GET_INFO}`); // Use the permission information of the current user to generate a routing table with corresponding permissions const allowRouters = await store.dispatch(`user/${GENERATE_ROUTES}`, info); if (allowRouters) { return { ...to, replace: true }; } return false; } }); router.afterEach(() => {});
3. Add interceptor
// request interceptor const requestHandler = ( config: AxiosRequestConfig, ): AxiosRequestConfig | Promise<AxiosRequestConfig> => { const savedToken = localStorage.get(STORAGE_TOKEN_KEY); // If the token exists // Let each request carry a custom token. Please modify it according to the actual situation if (savedToken) { config.headers[REQUEST_TOKEN_KEY] = savedToken; } config.headers[language] = 'cn'; return config; };
4. Login test
[LOGIN]({ commit }, info: LoginParams) { return new Promise((resolve, reject) => { // call ajax postAccountLogin(info) .then(res => { commit(SET_TOKEN, res.token); resolve(res); }) .catch(error => { reject(error); }); }); },
There is a lot of code, and the writing is miscellaneous. I'm afraid I'll forget. The cookie used in the previous project has been modified on this basis. Thank you for your help.
Link: VUE SPRINGBOOT implements TOKEN login and access verification.
Link: User login and token authentication in Vue project.