springmvc跨域+token验证

  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,如果没有就直接报错

在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位编码的。

				
时间: 2024-11-11 02:20:05

springmvc跨域+token验证的相关文章

JWT跨域身份验证解决方案

JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案.本文介绍JWT的原理和用法. 1. 当前跨域身份验证的问题 Internet服务无法与用户身份验证分开.一般过程如下.1.用户向服务器发送用户名和密码.2.验证服务器后,相关数据(如用户角色,登录时间等)将保存在当前会话中.3.服务器向用户返回session_id,session信息都会写入到用户的Cookie.4.用户的每个后续请求都将通过在Cookie中取出session_id传给服务器.5.服务器收到session_

SpringMvc跨域支持

SpringMvc跨域支持 在controller层加上注解@CrossOrigin可以实现跨域 该注解有两个参数 1,origins  : 允许可访问的域列表 2,maxAge:飞行前响应的缓存持续时间的最大年龄(以秒为单位). 原文地址:https://www.cnblogs.com/cailijuan/p/8656484.html

SpringMvc 跨域处理

导读 由于浏览器对于JavaScript的同源策略的限制,导致A网站(Ajax请求)不能通过JS去访问B网站的数据,于是跨域问题就出现了. 跨域指的是域名.端口.协议的组合不同就是跨域. http://www.chenyanbin.com/ https://www.chenyanbin.com/ http://www.chenyanbin.cn http://www.chenyanbin.com:8080/ 为什么要有同源策略? 举个例子:比如一个黑客程序,他利用IFrame把真正的银行登录页面

springmvc跨域(转)

跨域资源共享 CORS 详解 原文链接:http://www.ruanyifeng.com/blog/2016/04/cors.html 作者: 阮一峰 日期: 2016年4月12日 CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing). 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制. 本文详细介绍CORS的内部机制. (图片说明:摄于阿联酋艾因(Al Ain)的绿洲公园)

(H5)FormData+AJAX+SpringMVC跨域异步上传文件

最近都没时间整理资料了,一入职就要弄懂业务,整天被业务弄得血崩. 总结下今天弄了一个早上的跨域异步上传文件.主要用到技术有HTML5的FormData,AJAX,Spring MVC. 首先看下上传页面: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script type="text/javascript" src="js/plugins/

跨域身份验证

传统身份验证的方法session 当用户请求登录的时候,如果没有问题,我们在服务端生成一条记录,这个记录里可以说明一下登录的用户是谁,然后把这条记录的 ID 号发送给客户端,客户端收到以后把这个 ID 号存储在 Cookie 里,下次这个用户再向服务端发送请求的时候,可以带着这个 Cookie ,这样服务端会验证一个这个 Cookie 里的信息,看看能不能在服务端这里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端. 这些 Session 可能会存储在内存,磁

Springmvc跨域请求

@CrossOrigin(origins = "*", maxAge = 3600) public abstract class BaseController { @InitBinder public void initBinder(ServletRequestDataBinder binder) { // 自动转换日期类型的字段格式 binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateF

跨域验证cookie与缓存控制

1. 是否能跨域完全取决于浏览器控制,浏览器可以直接拒绝发送跨域请求(服务器根本收不到),也可以发送给服务器等接收到返回信息后决定是否让它被读取. 2. 服务器并不能辨别请求是从哪个源发过来的,只有在客户端能够知道,因此浏览器承担起了这个责任,对于跨域ajax请求会自动添加origin头部,让服务器能够知道请求来自一个陌生的源.如果服务器觉得该源可信任,需要在response-header中增加字段Access-Control-Allow-Origin,告诉浏览器可以让请求源读取返回的报文.(也

跨域解决方案与提交订单

跨域解决方案与提交订单 课程目标  目标 1:掌握跨域请求 CORS 解决方案目标 2:完成结算页收货人地址选择功能目标 3:完成结算页支付方式选择目标 4:完成结算页商品清单功能目标 5:完成保存订单功能 1.商品详细页跨域请求  1.1 需求分析  从商品详细页点击“加入购物车”按钮,将当前商品加入购物车,并跳转到购物车页面. 1.2 JS 跨域请求  这里说的 js 跨域是指通过 js 在不同的域之间进行数据传输或通信,比如用 ajax 向一个不同的域请求数据,或者通过 js 获取页面中不