Article source: https://www.cnblogs.com/minsons/p/7058837.html
This is the second lesson of the app background framework construction, mainly for the cross-domain application of the app, explaining how to configure cross-domain services; secondly, explaining how to perform token verification, setting token verification through the interceptor and setting the token to the http message . The main ones are as follows:
1) App background cross-domain settings2) Set the token in the HTTP message header in the interceptor
3) Token generation implementation
========================= ===================================================== ==========================
1, app background cross-domain settings
1.1 springmvc4 has cross-domain processing directly in the request mapping, just add A @CrossOrign()
copy code
@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));
} If the
copy code
intercepts the global request path, it needs to be declared in the configuration class:
copy code
@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 defined, you can also set it directly to /api/* or the like, and allowedOrigins can also be matched to *,
you can refer to the official documentation: https://spring.io /guides/gs/rest-service-cors/
1.2 Processing through filter
In fact, spring's interceptors can also handle cross-domain problems, but the support for post+json is not very good, and the support of interceptors will be better Some:
First, define the interceptor: 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 code
Second , set the filter in web.xml:
copy code
<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> 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>
3) My configuration class configuration:
copy code
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/");
}
//允许跨域的接口
@Override
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 code
2) Set the token
in the interceptor It is relatively simple to set the token in the interceptor, I took it directly, see the configuration :
Interceptor class: HeaderTokenInterceptor.javaCopy
code
package com.ouyang.teson.intercept;
import com.ouyang.teson.WebConfig;
import com.ouyang.teson.util.JwtUtil;
import org.slf4j.Logger
; .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':'缺少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 httpServletResponse, Object o, Exception e) throws Exception {
}
// 检测到没有token,直接返回不验证
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 code
httpServletResponse.setHeader("token",token) is to set the token information of the header that returns the response. Every time it intercepts, it will check whether there is a token. If not, it will report an error directly.
About the result returned by the app background , in actual development, it is necessary to return the data format uniformly, which will be discussed in the next section.
Add the following two methods to the webconfig.java class:
@Override
public
void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getTokenHeader())
.addPathPatterns("/api/*")
.excludePathPatterns(
"/robots.txt");
}
//token interceptor in header
@Bean
public HandlerInterceptor getTokenHeader(){
return new HeaderTokenInterceptor( );
}
Copy code
3) Token implementation The
token implementation uses the jwt component to generate the token. If you want to generate the token through md5 or rsa encryption, it is also relatively simple, but the token needs to be cached and verified each time. Update token. Updating the token is mainly to update the time contained in the token to prevent the token from expiring. If you use a token, you don't need to store the cache. After the login verification is successful, we will generate a token. This token can also contain basic information such as the user's id, and we can verify his expiration time, id and other information.
For the introduction of jwt components, you can go to see the introduction of jwt of my java components.
Go directly to the topic: maven
needs to import
<!-- java-web-token authentication authorization-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<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 code
jjwt is mainly to further encapsulate jwt, which can quickly develop web token authentication.
jwt tool class: jwtUtil.java
copy code
package com.ouyang.teson.util;
import io.jsonwebtoken.Claims;
import io.
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;
}
}
复制代码
Regarding the interceptor's processing token and updating the token, the code has been given above and will not be listed here. Let's take a look at a simple control class, just for learning, if you want to use it in the production environment, you have to configure and test it.
Login control method:
copy code
@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 code
There is no verification here, just simply based on the account password, After the token is generated, redirect; the next task is handed over to the interceptor, the interceptor will intercept the request under /api/*, and then the request parameter with token will verify the token, update the token, and put the token in the header inside.
Here you can see that the token string has two points. It is best to base64-encode the token generated by jwt. There is updateTokenBase64Code (String token) in jwtUtil.java to process the token for base64-encoding. The processing speed is quite fast.