This is the second lesson of app background framework construction. It mainly explains how to configure cross-domain services for the application of APP applications. Secondly, it explains how to implement token authentication, setting token authentication through interceptor and setting token in HTTP packet. The main ones are as follows:
1) Cross-domain setting of app background
2) Set the token in the HTTP packet header in the interceptor
3) Token generation and implementation
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
1. Cross-domain setting of app background
1.1 SpringMvC4 has cross-domain handling directly in the request map, simply by adding @crossorign ()
@CrossOrigin(origins = "http://localhost:9000") @GetMapping("/greeting") public Greeting greeting(@RequestParam(required=false, defaultValue="World") String name) { System.out.println("==== in greeting ===="); return new Greeting(counter.incrementAndGet(), String.format(template, name)); }Copy the code
Interception of the global request path needs to be declared in the configuration class:
@Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000"); }}; }Copy the code
“/ greeting-Javaconfig” is the request path you define. You can also set it to something like/API /*. AllowedOrigins can also match to *
You can refer to the official document: https://spring.io/guides/gs/rest-service-cors/
1.2 Using a Filter filter
Spring’s interceptor can also handle cross-domain issues, but post+ JSON support is not very good. Interceptor support is better:
First, define interceptors:
public class CrossFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (request.getHeader("Access-Control-Request-Method") ! = null && "OPTIONS".equals(request.getMethod())) { // CORS "pre-flight" request response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); response.addHeader("Access-Control-Allow-Headers", "Content-Type"); response.addHeader("Access-Control-Max-Age", "1800"); //30 min } filterChain.doFilter(request, response); }}Copy the code
Second, set the filter in web.xml:
<filter>
<filter-name>cors</filter-name>
<filter-class>cn.***.filter.CrossFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>Copy the code
Of course, spring4 appalication. XML can also be configured as:
<mvc:cors>
<mvc:mapping path="/**" allowed-origins="*" allow-credentials="true" max-age="1800" allowed-methods="GET,POST,OPTIONS"/>
</mvc:cors>Copy the code
3) My configuration class configuration:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.*; import org.springframework.core.env.Environment; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.*; import org.springframework.web.servlet.mvc.Controller; import org.springframework.web.servlet.view.InternalResourceViewResolver; import java.util.ArrayList; import java.util.List; /** * Created by ThinkPad on 2017/6/15. */ @Configuration @EnableWebMvc @ComponentScan(basePackages = {"com.ouyang.teson"},useDefaultFilters = true) @PropertySource({"classpath:teson.properties"}) public class WebConfig extends WebMvcConfigurerAdapter{ private final static Logger logger = LoggerFactory.getLogger(WebConfig.class); public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/jsp/function/"); viewResolver.setSuffix(".jsp"); return viewResolver; } @override public void addResourceHandlers(ResourceHandlerRegistry registry) { logger.info("addResourceHandlers"); registry.addResourceHandler("/static/**").addResourceLocations("/WEB-INF/static/"); } public void addCorsMappings(CorsRegistry registry) {public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/*").allowedOrigins("*") .allowCredentials(false) .allowedMethods("GET", "POST", "DELETE", "PUT") .allowedHeaders("Access-Control-Allow-Origin","Access-Control-Allow-Headers","Access-Control-Allow-Methods" ,"Access-Control-Max-Age")
.exposedHeaders("Access-Control-Allow-Origin")
.maxAge(3600);
}
}Copy the code
2) Set the token in the interceptor
Setting a token in an interceptor is a bit easier, so I’ll just go through the configuration:
Interceptor class: HeaderTokenInterceptor. Java
package com.ouyang.teson.intercept; import com.ouyang.teson.WebConfig; import com.ouyang.teson.util.JwtUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * Created by ThinkPad on 2017/6/20. */ public class HeaderTokenInterceptor implements HandlerInterceptor { private final static Logger logger = LoggerFactory.getLogger(HeaderTokenInterceptor.class); @Autowired JwtUtil jwtUtil; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { // String contentPath=httpServletRequest.getContextPath(); // System.out.println("contenxPath:"+contentPath); String requestURI=httpServletRequest.getRequestURI(); String tokenStr=httpServletRequest.getParameter("token"); String token=""; if(requestURI.contains("/api/")){ token=httpServletRequest.getHeader("token"); if(token==null && tokenStr==null){ System.out.println("real token:======================is null"); String STR ="{'errorCode':801,'message':' missing token ','data':null}"; dealErrorReturn(httpServletRequest,httpServletResponse,str); return false; } if(tokenStr! =null){ token=tokenStr; } token=jwtUtil.updateToken(token); System.out.println("real token:=============================="+token); System.out.println("real ohter:=============================="+httpServletRequest.getHeader("Cookie")); } httpServletResponse.setHeader("token",token); /* httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); httpServletResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT"); */ return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest HttpServletRequest, HttpServletResponse, Object O, Exception Public void dealErrorReturn(HttpServletRequest HttpServletRequest, HttpServletResponse httpServletResponse,Object obj){ String json = (String)obj; PrintWriter writer = null; httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("text/html; charset=utf-8"); try { writer = httpServletResponse.getWriter(); writer.print(json); } catch (IOException ex) { logger.error("response error",ex); } finally { if (writer ! = null) writer.close(); }}}Copy the code
HttpServletResponse. SetHeader (" token ", token) is set to return to the response header token information, every time I intercept, will check whether there is a token, if there is no error directly
Copy the code
As for the results returned by app background, the actual development needs to unify the returned data format, which will be discussed in the next section.Copy the code
Add the following two methods to the webconfig.java class:
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(getTokenHeader()) .addPathPatterns("/api/*") .excludePathPatterns( "/robots.txt"); @bean public HandlerInterceptor getTokenHeader(){return new HeaderTokenInterceptor(); }Copy the code
3) Implementation of token
The implementation of token uses JWT component to generate token. If you want to generate token by MD5 or RSA encryption, it is also relatively easy, but the token needs to be cached and verified every time, and the token is updated after verification. Updating a token mainly updates the time contained in the token to prevent the token from expiring. If the token is used, there is no need to store the cache. After the login authentication is successful, we will generate the token, which can also contain the user’s ID and other basic information, so that we can verify his expiration time, ID and other information.
For an introduction to JWT components, check out my JWT introduction to Java Components.
Straight to the point:
Maven import required
<! --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> The < version > 3.2.0 < / version > < / dependency > <! -- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt --> <dependency> <groupId>io.jsonwebtoken</groupId> < artifactId > JJWT < / artifactId > < version > 0.7.0 < / version > < / dependency >Copy the code
JJWT is further encapsulation of JWT, which can quickly develop token authentication for web.
JWT utility class: jwtutil.java
package com.ouyang.teson.util; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; import java.security.Key; import java.util.Date; /** * Created by ThinkPad on 2017/6/17. */ @Component public class JwtUtil { public static String sercetKey="mingtianhenganghao"; public final static long keeptime=1800000; /* @Value("${token.sercetKey}") public static String sercetKey; @Value("${token.keeptime:30000}") public static long keeptime; */ public static String generToken(String id, String issuer, String subject){ long ttlMillis=keeptime; SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(sercetKey); Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); JwtBuilder builder = Jwts.builder().setId(id) .setIssuedAt(now); if(subject! =null){ builder.setSubject(subject); } if(issuer! =null){ builder.setIssuer(issuer); } builder .signWith(signatureAlgorithm, signingKey); if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp); } return builder.compact(); } public String updateToken(String token){ try { Claims claims=verifyToken(token); String id=claims.getId(); String subject=claims.getSubject(); String issuer=claims.getIssuer(); Date date = claims.getExpiration(); return generToken(id, issuer, subject); }catch (Exception ex){ ex.printStackTrace(); } return "0"; } public String updateTokenBase64Code(String token) { BASE64Encoder base64Encoder=new BASE64Encoder(); BASE64Decoder decoder = new BASE64Decoder(); try { token=new String(decoder.decodeBuffer(token),"utf-8"); Claims claims=verifyToken(token); String id=claims.getId(); String subject=claims.getSubject(); String issuer=claims.getIssuer(); Date date = claims.getExpiration(); String newToken = generToken(id, issuer, subject); return base64Encoder.encode(newToken.getBytes()); }catch (Exception ex){ ex.printStackTrace(); } return "0"; } public static Claims verifyToken(String token){ Claims claims = Jwts.parser() .setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey)) .parseClaimsJws(token).getBody(); return claims; }}Copy the code
The code for interceptor handling tokens and updating tokens is given above and will not be listed here. Let’s take a look at the simple control class, which is just for learning, but will need to be configured and tested if it is to be used in production.
Login control method:
@RequestMapping("/login")
public String login(String name,String password, Model model){
if(name==null || password==null){
return "error";
}
String token = jwtUtil.generToken("xiaoming",null,null);
model.addAttribute("token", token);
return "redirect:/api/liu";
}Copy the code
There is no authentication here, but simply generates the token according to the account password and redirects. The interceptor then intercepts the request under/API /*, and the request argument with the token validates the token, updates the token, and puts the token into the header.
Jwtutil. Java has updateTokenBase64Code(String Token), which is used to encode tokens in base64 bits. That’s pretty fast.
And finally, the code for the app background framework will come out in lecture 5 or so. I don’t have enough time to blog.