zoukankan      html  css  js  c++  java
  • springmvc跨域+token验证(app后台框架搭建二)

    这是app后台框架搭建的第二课,主要针对app应用是跨域的运用,讲解怎么配置跨域服务;其次讲解怎么进行token验证,通过拦截器设置token验证和把token设置到http报文中。主要有如下:

          1)app后台跨域设置

         2)拦截器中设置http报文header中token

         3)token的生成实现

    ====================================================================================================

     1,app后台跨域的设置

        1.1   springmvc4 有直接在请求映射中对跨域的处理,只需加一个@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));
        }

       对全局请求路径的拦截的,则需要在配置类里声明:

      @Bean
        public WebMvcConfigurer corsConfigurer() {
            return new WebMvcConfigurerAdapter() {
                @Override
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
                }
            };
        }

     “/greeting-javaconfig” 则是你定义的请求路径了,你也可以直接设置为 /api/* 之类的,allowedOrigins也可以匹配成

     可以参考官方文档:https://spring.io/guides/gs/rest-service-cors/ 

     1.2 通过filter过滤器进行处理

         其实,spring的拦截器也是可以处理跨域的问题,但对于post+json的支持不是很好,用拦截器的支持会好一些:

         首先,定义拦截器:

    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);
        }
    }

    其次,在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>

     当然spring4  appalication.xml 也可以配置成:

    <mvc:cors>
            <mvc:mapping path="/**" allowed-origins="*" allow-credentials="true" max-age="1800" allowed-methods="GET,POST,OPTIONS"/>
        </mvc:cors>

    3)我的配置类配置:

    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);
    }


    }

    2) 在拦截器中设置token

        在拦截器中设置token这个比较简单,我就直接带过了,看配置:

      拦截器类: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':'缺少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();
            }
        }
    
    
    }
    httpServletResponse.setHeader("token",token)是设置返回response的header的token信息,每一次拦截的时候,会查看是否有token,如果没有就直接报错

    关于app 后台返回的结果,在实际的开发中需要统一返回数据格式,这个会在下一节中讲到。

    在webconfig.java 类中添加以下两个方法:

     @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(getTokenHeader())
                    .addPathPatterns("/api/*")
                    .excludePathPatterns(
                            "/robots.txt");
        }
    
        //token 在header的拦截器
        @Bean
        public HandlerInterceptor getTokenHeader(){
            return new HeaderTokenInterceptor();
        }

    3) token的实现

     token的实现使用jwt组件生成token,如果想要自己通过md5,或者rsa加密生成token也比较简便了,只是这个token要缓存起来,每次进行验证,验证完更新token。更新token主要是更新token里包含的时间,防止token过期。如果使用token的话,可以不用存放缓存,对于登陆验证成功后,我们会生成token,这个token还能带有用户的id等基本信息,我们就可以验证他的过期时间,id等信息。

     关于jwt 组件的介绍,可以去看看我的 java组件的jwt的介绍。

    直接进入主题了:

    maven需要导入

    <!-- java-web-token 验证授权-->
        <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>

    jjwt 主要是对jwt进一步封装,可以快速开发web的token认证。

        jwt工具类: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;
        }
    
    
    
    
    
    }

      关于拦截器的处理token,及更新token,上面已经给出代码,这里不再列出。来看一下简单的控制类,仅供学习,如果要运用到生产环境还得各种配置和测试。

      登陆的控制方法:

    @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";
    
        }

    这里没有做验证,只是简单根据账户密码,生成token后,重定向;接下来的任务就交给拦截器了,拦截器会拦截/api/* 下的请求,然后请求参数有token的会验证token,并更新token,并把token放到header里。

    这里可以看到token字符串有两个点,最好把jwt生成的token进行base64位编码,jwtUtil.java里有updateTokenBase64Code(String token)就是处理token进行base64位编码的。处理速度还是蛮快的。

    最后,app后台框架的代码会在第五讲左右,把代码放出来。没有那么充足时间,写博客。

  • 相关阅读:
    MISP版本嵌入式QT编译时出现mips-linux-gcc command not found
    数据传输对象(DTO)介绍及各类型实体比较
    signalR例子
    WebAPI GET和POST请求的几种方式
    github教程
    Asp.net MVC + EF + Spring.Net 项目实践3
    SpringMVC
    SignalR
    SignalR的实时高频通讯
    开发视频教程
  • 原文地址:https://www.cnblogs.com/minsons/p/7058837.html
Copyright © 2011-2022 走看看