springmvc cross-domain + token verification (app background framework construction 2)

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 settings
     2) 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.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325606054&siteId=291194637