• springmvc的文件上传和JWT图形验证码


    相关pom依赖

    <dependency>
          <groupId>commons-fileupload</groupId>
          <artifactId>commons-fileupload</artifactId>
          <version>1.3.3</version>
    </dependency>

    springmvc-servlet.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!-- 通过context:component-scan元素扫描指定包下的控制器-->
        <!--1) 扫描com.javaxl.zf及子子孙孙包下的控制器(扫描范围过大,耗时)-->
        <aop:aspectj-autoproxy/>
        <context:component-scan base-package="com.jt"/>
    
        <!--2) 此标签默认注册DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter -->
        <!--两个bean,这两个bean是spring MVC为@Controllers分发请求所必须的。并提供了数据绑定支持,-->
        <!--@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,读写XML的支持(JAXB),读写JSON的支持(Jackson)-->
        <mvc:annotation-driven></mvc:annotation-driven>
    
        <!--3) ViewResolver -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- viewClass需要在pom中引入两个包:standard.jar and jstl.jar -->
            <property name="viewClass"
                      value="org.springframework.web.servlet.view.JstlView"></property>
            <property name="prefix" value="/WEB-INF/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <!-- 必须和用户JSP 的pageEncoding属性一致,以便正确解析表单的内容 -->
            <property name="defaultEncoding" value="UTF-8"></property>
            <!-- 文件最大大小(字节) 1024*1024*50=50M-->
            <property name="maxUploadSize" value="52428800"></property>
            <!--resolveLazily属性启用是为了推迟文件解析,以便捕获文件大小异常-->
            <property name="resolveLazily" value="true"/>
        </bean>
    
        <!--4) 单独处理图片、样式、js等资源 -->
        <!--<mvc:resources location="/css/" mapping="/css/**"/>-->
            <!--<mvc:resources location="/images/" mapping="/images/**"/>-->
        <!--<mvc:resources location="/js/" mapping="/js/**"/>-->
        <mvc:resources location="/static/" mapping="/static/**"/>
    
    </beans>

    upload.jsp

    <%--
      Created by IntelliJ IDEA.
      User: 蒋涛
      Date: 2019/10/30
      Time: 11:08
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>springmvc文件上传</title>
    </head>
    <body>
    <form action="${pageContext.request.contextPath}/book/upload" method="post" enctype="multipart/form-data">
        请选择文件:<input type="file" name="xxx">
        <input type="submit" value="OK">
    </form>
    </body>
    </html>

    BookController

    @RequestMapping("/upload")
        public String upload(HttpServletRequest req, MultipartFile xxx){
            String fileName=xxx.getOriginalFilename();
            String contentType=xxx.getContentType();
            try {
                FileUtils.copyInputStreamToFile(xxx.getInputStream(),new File("F:/jtredis/mybatis04/src/main/webapp/static/img/"+fileName));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return  "redirect:/book/list";
        }

     

     

     

    JWT图形验证码

     

     首先我们来导入POM依赖

    <!--引入JWT依赖,由于是基于Java,所以需要的是java-jwt -->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.9.1</version>
            </dependency>
            <dependency>
                <groupId>com.auth0</groupId>
                <artifactId>java-jwt</artifactId>
                <version>3.4.0</version>
            </dependency>

     先导入一下后端代码

    User

    package com.jt.model;
    
    public class User {
        private String uname;
    
        private String pwd;
    
        public User(String uname, String pwd) {
            this.uname = uname;
            this.pwd = pwd;
        }
    
        public User() {
            super();
        }
    
        public String getUname() {
            return uname;
        }
    
        public void setUname(String uname) {
            this.uname = uname;
        }
    
        public String getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    }

     UserMapper

    package com.jt.mapper;
    
    
    import com.jt.model.User;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public interface UserMapper {
        int deleteByPrimaryKey(String uname);
    
        int insert(User record);
    
        int insertSelective(User record);
    
        User selectByPrimaryKey(String uname);
    
        int updateByPrimaryKeySelective(User record);
    
        int updateByPrimaryKey(User record);
    
        User login(User user);
    }

     UserMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.jt.mapper.UserMapper" >
     <!-- <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>-->
      <!--<cache type="com.jt.util.RedisCache"></cache>-->
    
    
        <select id="login" resultType="com.jt.model.User" parameterType="com.jt.model.User">
            select
         *
            from t_vue_user
            where uname = #{uname} and pwd = #{pwd}
        </select>
    
    
    </mapper>

     CorsFilter

    package com.jt.util;
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * 配置tomcat允许跨域访问
     *
     * @author Administrator
     *
     */
    public class CorsFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
                throws IOException, ServletException {
            HttpServletResponse resp = (HttpServletResponse) servletResponse;
            HttpServletRequest req = (HttpServletRequest) servletRequest;
    
            // Access-Control-Allow-Origin就是我们需要设置的域名
            // Access-Control-Allow-Headers跨域允许包含的头。
            // Access-Control-Allow-Methods是允许的请求方式
            resp.setHeader("Access-Control-Allow-Origin", "*");// *,任何域名
            resp.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE");
            // resp.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With,
            // Content-Type, Accept");
            // 允许客户端,发一个新的请求头jwt
            //允许客户端发送一个新的请求头
            resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, jwt, verificationJwt");
            //允许客户端处理一个新的响应头jwt
            resp.setHeader("Access-Control-Expose-Headers", "jwt");
            resp.setHeader("Access-Control-Expose-Headers", "verificationJwt");
            // String sss = resp.getHeader("Access-Control-Expose-Headers");
            // System.out.println("sss=" + sss);
    
            // 允许请求头Token
            // httpResponse.setHeader("Access-Control-Allow-Headers","Origin,X-Requested-With,
            // Content-Type, Accept, Token");
            // System.out.println("Token=" + req.getHeader("Token"));
    
            if ("OPTIONS".equals(req.getMethod())) {// axios的ajax会发两次请求,第一次提交方式为:option,直接返回即可
                return;
            }
            filterChain.doFilter(servletRequest, servletResponse);
        }
    
        @Override
        public void destroy() {
    
        }
    }

     ImageUtil

    package com.jt.util;
    import sun.misc.BASE64Encoder;
    
    import javax.imageio.ImageIO;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.util.Random;
    
    public class ImageUtil {
    
        /**
         * 根据指定的随机数 生成验证码图片 转 base64
         * @param word 要生存的验证码随机字符串
         * @param width 图片宽度
         * @param height 图片高度
         * @return base64 格式生成的验证码图片
         * @throws IOException
         */
        public static String createImageWithVerifyCode(String word, int width, int height) throws IOException {
            String png_base64="";
            //绘制内存中的图片
            BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
            //得到画图对象
            Graphics graphics = bufferedImage.getGraphics();
            //绘制图片前指定一个颜色
            graphics.setColor(getRandColor(160,200));
            graphics.fillRect(0,0,width,height);
            //绘制边框
            graphics.setColor(Color.white);
            graphics.drawRect(0, 0, width - 1, height - 1);
            // 步骤四 四个随机数字
            Graphics2D graphics2d = (Graphics2D) graphics;
            graphics2d.setFont(new Font("宋体", Font.BOLD, 18));
            Random random = new Random();
            // 定义x坐标
            int x = 10;
            for (int i = 0; i < word.length(); i++) {
                // 随机颜色
                graphics2d.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
                // 旋转 -30 --- 30度
                int jiaodu = random.nextInt(60) - 30;
                // 换算弧度
                double theta = jiaodu * Math.PI / 180;
                // 获得字母数字
                char c = word.charAt(i);
                //将c 输出到图片
                graphics2d.rotate(theta, x, 20);
                graphics2d.drawString(String.valueOf(c), x, 20);
                graphics2d.rotate(-theta, x, 20);
                x += 30;
            }
            // 绘制干扰线
            graphics.setColor(getRandColor(160, 200));
            int x1;
            int x2;
            int y1;
            int y2;
            for (int i = 0; i < 30; i++) {
                x1 = random.nextInt(width);
                x2 = random.nextInt(12);
                y1 = random.nextInt(height);
                y2 = random.nextInt(12);
                graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
            }
            graphics.dispose();// 释放资源
            ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
            ImageIO.write(bufferedImage, "png", baos);//写入流中
            byte[] bytes = baos.toByteArray();//转换成字节
            BASE64Encoder encoder = new BASE64Encoder();
            png_base64 = encoder.encodeBuffer(bytes).trim();
            png_base64 = png_base64.replaceAll("
    ", "").replaceAll("
    ", "");//删除 
    
            return png_base64;
        }
    
    
    
        /**设置随机颜色*/
        private static Color getRandColor(int fc, int bc) {
            // 取其随机颜色
            Random random = new Random();
            if (fc > 255) {
                fc = 255;
            }
            if (bc > 255) {
                bc = 255;
            }
            int r = fc + random.nextInt(bc - fc);
            int g = fc + random.nextInt(bc - fc);
            int b = fc + random.nextInt(bc - fc);
            return new Color(r, g, b);
        }
    
    }

     JSONResult

    package com.jt.util;
    
    public class JSONResult {
    
        // 响应业务状态
        private Integer status;
    
        // 响应消息
        private String msg;
    
        // 响应中的数据
        private Object data;
    
        private String ok; // 不使用
    
        public static JSONResult build(Integer status, String msg, Object data) {
            return new JSONResult(status, msg, data);
        }
    
        public static JSONResult ok(Object data) {
            return new JSONResult(data);
        }
    
        public static JSONResult ok() {
            return new JSONResult(null);
        }
    
        public static JSONResult errorMsg(String msg) {
            return new JSONResult(500, msg, null);
        }
    
        public static JSONResult errorMap(Object data) {
            return new JSONResult(501, "error", data);
        }
    
        public static JSONResult errorTokenMsg(String msg) {
            return new JSONResult(502, msg, null);
        }
    
        public static JSONResult errorException(String msg) {
            return new JSONResult(555, msg, null);
        }
    
        public JSONResult() {
    
        }
    
        public JSONResult(Integer status, String msg, Object data) {
            this.status = status;
            this.msg = msg;
            this.data = data;
        }
    
        public JSONResult(Object data) {
            this.status = 200;
            this.msg = "OK";
            this.data = data;
        }
    
        public Boolean isOK() {
            return this.status == 200;
        }
    
        public Integer getStatus() {
            return status;
        }
    
        public void setStatus(Integer status) {
            this.status = status;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    
        public String getOk() {
            return ok;
        }
    
        public void setOk(String ok) {
            this.ok = ok;
        }
    
    }

     JwtUtils

    package com.jt.util;
    
    import java.util.Date;
    import java.util.Map;
    import java.util.UUID;
    
    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;
    
    import org.apache.commons.codec.binary.Base64;
    
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    
    /**
     * JWT验证过滤器:配置顺序 CorsFilte->JwtUtilsr-->StrutsPrepareAndExecuteFilter
     *
     */
    public class JwtUtils {
        /**
         * JWT_WEB_TTL:WEBAPP应用中token的有效时间,默认30分钟
         */
        public static final long JWT_WEB_TTL = 5 * 60 * 1000;
    
        /**
         * 将jwt令牌保存到header中的key
         */
        public static final String JWT_HEADER_KEY = "jwt";
        public static final String JWT_VERIFICATION_KEY = "verificationJwt";
    
        // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
        private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
        private static final String JWT_SECRET = "f356cdce935c42328ad2001d7e9552a3";// JWT密匙
        private static final SecretKey JWT_KEY;// 使用JWT密匙生成的加密key
    // private static final SecretKey JWT_VERIFICATION_KEY;// 使用JWT密匙生成的加密key
    
    
        static {
            byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);
            JWT_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
    //    这里我偷个懒,用户登录jwt密钥,与图形验证码jwt密钥搞成同一个
    //    JWT_VERIFICATION_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        }
    
        private JwtUtils() {
        }
    
        /**
         * 解密jwt,获得所有声明(包括标准和私有声明)
         *
         * @param jwt
         * @return
         * @throws Exception
         */
        public static Claims parseJwt(String jwt) {
            Claims claims = Jwts.parser().setSigningKey(JWT_KEY).parseClaimsJws(jwt).getBody();
            return claims;
        }
    
        /**
         * 创建JWT令牌,签发时间为当前时间
         *
         * @param claims
         *            创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
         * @param ttlMillis
         *            JWT的有效时间(单位毫秒),当前时间+有效时间=过期时间
         * @return jwt令牌
         */
        public static String createJwt(Map<String, Object> claims, long ttlMillis) {
            // 生成JWT的时间,即签发时间
            long nowMillis = System.currentTimeMillis();
    
            // 下面就是在为payload添加各种标准声明和私有声明了
            // 这里其实就是new一个JwtBuilder,设置jwt的body
            JwtBuilder builder = Jwts.builder()
                    // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                    .setClaims(claims)
                    // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                    // 可以在未登陆前作为身份标识使用
                    .setId(UUID.randomUUID().toString().replace("-", ""))
                    // iss(Issuser)签发者,写死
                    // .setIssuer("zking")
                    // iat: jwt的签发时间
                    .setIssuedAt(new Date(nowMillis))
                    // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
                    // .setSubject("{}")
                    // 设置签名使用的签名算法和签名使用的秘钥
                    .signWith(SIGNATURE_ALGORITHM, JWT_KEY)
                    // 设置JWT的过期时间
                    .setExpiration(new Date(nowMillis + ttlMillis));
    
            return builder.compact();
        }
    
        /**
         * 复制jwt,并重新设置签发时间(为当前时间)和失效时间
         *
         * @param jwt
         *            被复制的jwt令牌
         * @param ttlMillis
         *            jwt的有效时间(单位毫秒),当前时间+有效时间=过期时间
         * @return
         */
        public static String copyJwt(String jwt, Long ttlMillis) {
            Claims claims = parseJwt(jwt);
    
            // 生成JWT的时间,即签发时间
            long nowMillis = System.currentTimeMillis();
    
            // 下面就是在为payload添加各种标准声明和私有声明了
            // 这里其实就是new一个JwtBuilder,设置jwt的body
            JwtBuilder builder = Jwts.builder()
                    // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                    .setClaims(claims)
                    // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                    // 可以在未登陆前作为身份标识使用
                    //.setId(UUID.randomUUID().toString().replace("-", ""))
                    // iss(Issuser)签发者,写死
                    // .setIssuer("zking")
                    // iat: jwt的签发时间
                    .setIssuedAt(new Date(nowMillis))
                    // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
                    // .setSubject("{}")
                    // 设置签名使用的签名算法和签名使用的秘钥
                    .signWith(SIGNATURE_ALGORITHM, JWT_KEY)
                    // 设置JWT的过期时间
                    .setExpiration(new Date(nowMillis + ttlMillis));
            return builder.compact();
        }
    
        public static Claims validateJwtToken(String jwt) {
            Claims claims = null;
            try {
                if (null != jwt) {
                    claims = JwtUtils.parseJwt(jwt);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return claims;
        }
    }

     VerifyCodeUtil

    package com.jt.util;
    
    import java.util.Random;
    
    public class VerifyCodeUtil {
    
    
        /**生成N位数字和字母混合的验证码
         * @param  num 验证码位数
         * @return code 生成的验证码字符串*/
        public static String produceNumAndChar(int num){
            Random random = new Random();
            String code = "";
            String ch = "ABCDEFGHIJKLMNPQRSTUVWXYZ";
            String n = "123456789";
            for(int i=0;i<num;i++){
                int flag = random.nextInt(2);
                if(flag==0){//数字
                    code+=n.charAt(random.nextInt(n.length()));
                }else{//字母
                    code+=ch.charAt(random.nextInt(ch.length()));
                }
            }
            return code;
        }
    }

     Service

    package com.jt.service;
    
    
    import com.jt.model.User;
    
    public interface UserService {
        public User login(User user);
    }
    UserServiceImpl
    package com.jt.service.impl;
    
    import com.jt.mapper.UserMapper;
    import com.jt.model.User;
    import com.jt.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserMapper userMapper;
        @Override
        public User login(User user) {
            return userMapper.login(user);
        }
    }

    UserController.java

    package com.jt.controller;
    
    import com.jt.model.User;
    import com.jt.service.UserService;
    import com.jt.util.ImageUtil;
    import com.jt.util.JSONResult;
    import com.jt.util.JwtUtils;
    import com.jt.util.VerifyCodeUtil;
    import io.jsonwebtoken.Claims;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Controller;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    
    @Controller
    @RequestMapping("/vue/user")
    public class UserController {
        private static final String VERIFICATION_CODE = "verificationCode_";
    
        @Autowired
        private UserService userService;
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        @RequestMapping("/login")
        @ResponseBody
        public JSONResult login(User u, HttpServletRequest request, HttpServletResponse response){
            //获取用户输入的验证码
            String userVerificationCode = request.getParameter("verificationCode");
            //获取验证码jwt令牌
            String userJwt = request.getHeader(JwtUtils.JWT_VERIFICATION_KEY);
            //获取到保存在redis中的验证码
            Object redisVerificationCode =  redisTemplate.opsForValue().get(VERIFICATION_CODE + userJwt) ;
    
    //        这里存在两种情况:1、令牌超时   2、验证码超时
            if(StringUtils.isEmpty(redisVerificationCode)){
                return JSONResult.errorMsg("你的验证码已超时");
            }
    
            if(!redisVerificationCode.toString().equalsIgnoreCase(userVerificationCode)){
                return JSONResult.errorMsg("验证码错误");
            }
    
            User user = userService.login(u);
            //判断是否登录成功
            if(user != null){
                Map<String,Object> map=new HashMap<String, Object>();
                map.put("User", user);
                //这是颁发用户登录成功的jwt令牌
                String jwt= JwtUtils.createJwt(map, JwtUtils.JWT_WEB_TTL);
                response.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);
                return JSONResult.ok(user);
            }else {
                return JSONResult.errorMsg("密码或账户错误");
            }
    
        }
    
    
    
    
        /**生成图片验证码*/
        @RequestMapping("/verificationCode")
        @ResponseBody
        public String verificationCode(HttpServletRequest req, HttpServletResponse resp) throws IOException {
            System.out.println("验证码");
            //生成验证码随机数
            String word = VerifyCodeUtil.produceNumAndChar(4);
    //        获取用户的jwt令牌
            String userVerificationJwt = req.getHeader(JwtUtils.JWT_VERIFICATION_KEY);
            //验证码令牌
            Claims claims = JwtUtils.validateJwtToken(userVerificationJwt);
            if(claims == null){
                //如果用户令牌过期那么对应存放在redis中的数据也要清空
                if(!StringUtils.isEmpty(userVerificationJwt)){
                    redisTemplate.expire(VERIFICATION_CODE + userVerificationJwt, 1, TimeUnit.DAYS);
                }
                userVerificationJwt =  JwtUtils.createJwt(new HashMap<String, Object>() ,JwtUtils.JWT_WEB_TTL);
                //将jwt令牌放入 response head中
                resp.setHeader(JwtUtils.JWT_VERIFICATION_KEY, userVerificationJwt);
            }
            //刷新缓存,更新验证码
            redisTemplate.opsForValue().set(VERIFICATION_CODE + userVerificationJwt , word,60, TimeUnit.SECONDS);
            //生成图片
            String code = "data:image/png;base64," + ImageUtil.createImageWithVerifyCode(word, 116,40);;
            return code;
        }
    }

    web.xml

      <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--此参数可以不配置,默认值为:/WEB-INF/springmvc-servlet.xml-->
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/springmvc-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <!--web.xml 3.0的新特性,是否支持异步-->
        <async-supported>true</async-supported>
      </servlet>
      <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
      </servlet-mapping>

    前端

    State.js

    export default{
        
        
        resturantName: '飞歌餐馆',
        jwt:'',
        options: [],//存放tab页的容器
        activeIndex: '',//激活的tab页路由路径
        showName:'show',//tab页的标题
        role:"",//用来区分是否是因为左侧菜单被点击造成的路由路径发生改变,是:pass;不是:nopass
        verificationJwt:null, //这是用来保存用户等登录验证码jwt身份识别的
    }

    Mutations.js

    export default {
        setResturantName: (state, payload) => {
            state.resturantName = payload.resturantName;
        },
        setJwt: (state, payload) => {
            state.jwt = payload.jwt;
        },
        // 添加tabs(data包含了路由路径跟tab页名字)
        add_tabs(state, data) {
            this.state.options.push(data);
        },
        // 删除tabs    (route是路由路径)
        delete_tabs(state, route) {
            let index = 0;
            for (let option of state.options) {
                if (option.route === route) {
                    break;
                }
                index++;
            }
            this.state.options.splice(index, 1); //删除options里面下标为Index的一个数
        },
        // 设置当前激活的tab
        set_active_index(state, index) {
            this.state.activeIndex = index;
        },
        //设置tab页显示标题
        set_showName(state, name) {
            this.state.showName = name;
        },
        set_role(state, role) {
            this.state.role = role;
        },
        setVerificationJwt: (state, payload) => {
            state.verificationJwt = payload.verificationJwt;
        }
        
    }

    Getters.js

    export default{
        getResturantName:(state)=>{
            return state.resturantName;
        },
        getJwt:(state)=>{
            return state.jwt;
        },
        getResturantName: (state) => {
            return state.resturantName;
        },
        getJwt: (state) => {
            return state.jwt;
        },
        getShowName:(state) => {
            return state.showName;
        },
        getOptions:(state) => {
            return state.options;
        },
        getRole:(state) =>{
            return state.role;
        },
        getVerificationJwt:(state) =>{
            return state.verificationJwt;
        }
    }

    action.js

    /**
     * 对后台请求的地址的封装,URL格式如下:
     * 模块名_实体名_操作
     */
    export default {
        // 'SERVER': 'http://localhost:8080/T216_SSH', //服务器
        'SERVER': 'http://localhost:8080', //服务器
        // 'SYSTEM_USER_DOLOGIN': '/vue/userAction_login.action', //用户登陆
        'SYSTEM_USER_DOLOGIN': '/vue/user/login', //用户登陆
        'VERIFICATION': '/vue/user/verificationCode', //用户登陆
        'SYSTEM_USER_DOREG': '/vue/userAction_reg.action', //用户注册
        'SYSTEM_MENU_TREE': '/vue/treeNodeAction.action', //左侧树形菜单加载
        'SYSTEM_ARTICLE_LIST': '/vue/articleAction_list.action', //文章列表
        'SYSTEM_ARTICLE_ADD': '/vue/articleAction_add.action', //文章新增
        'SYSTEM_ARTICLE_EDIT': '/vue/articleAction_edit.action', //文章修改
        'SYSTEM_ARTICLE_DEL': '/vue/articleAction_del.action', //文章删除
        'SYSTEM_USER_GETASYNCDATA': '/vue/userAction_getAsyncData.action', //vuex中的异步加载数据
        'getFullPath': k => { //获得请求的完整地址,用于mockjs测试时使用
            return this.SERVER + this[k];
        }
    }

    http.js

    // 请求拦截器
    axios.interceptors.request.use(function(config) {
        //设置验证码jwt令牌
        let verificationJwt = window.vm.$store.getters.getVerificationJwt;
        if (verificationJwt) {
            config.headers['verificationJwt'] = verificationJwt;
        }
    
        var jwt = window.vm.$store.getters.getJwt;
        config.headers['jwt'] = jwt;
        return config;
    }, function(error) {
        return Promise.reject(error);
    });
    
    // 响应拦截器
    axios.interceptors.response.use(function(response) {
        // debugger;
        //保存验证码jwt令牌
        let verificationjwt = response.headers['verificationjwt'];
        if (verificationjwt) {
            window.vm.$store.commit('setVerificationJwt', {
                verificationJwt: verificationjwt
            });
        }
        
        var jwt = response.headers['jwt'];
        if (jwt) {
            window.vm.$store.commit('setJwt', {
                jwt: jwt
            });
        }
        return response;
    }, function(error) {
        return Promise.reject(error);
    });

    Login.vue

    <template>
      <div class="login-wrap">
        <el-form class="login-container">
          <h1 class="title">用户登录</h1>
          <el-form-item label="">
            <el-input type="text" v-model="userName" placeholder="请输入登录账号" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="">
            <el-input type="password" v-model="userPwd" placeholder="请输入登录密码" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="">
            <el-row>
              <el-col :span="16">
                <el-input type="text" v-model="verificationCode" placeholder="请输入验证码" autocomplete="off"></el-input>
              </el-col>
              <el-col :span="8">
                <img id="img" :src="verificationCodeSrc" width="116px" height="40px" @click="changeVerificationCode" >
              </el-col>
            </el-row>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" style=" 100%;" @click="doSubmit">登  录</el-button>
          </el-form-item>
          <el-row style="text-align: center; margin-top: -15;">
            <el-link type="primary">忘记密码</el-link>
            <el-link type="primary" @click="gotoRegister">用户注册</el-link>
          </el-row>
        </el-form>
      </div>
    </template>
    
    
    <script>
        export default {
            name: 'Login',
            data: function() {
                return {
                    userName: null,
                    userPwd: null,
                    verificationCode:null,
                    verificationCodeSrc:null
                }
            },
            methods: {
                gotoRegister:function(){
                    this.$router.push('/Register');
                },
                doSubmit: function() {
                    let params = {
                        uname: this.userName,
                        pwd: this.userPwd,
                        verificationCode: this.verificationCode
                    };
                    let url = this.axios.urls.SYSTEM_USER_DOLOGIN;
    
                    this.axios.post(url, params).then(resp => {
                        if(resp.data.status==200) {
                            //提示登录成功
                            this.$message({
                                message: resp.data.msg,
                                type: 'success'
                            });
                            //跳转路由
                            this.$router.push({
                                path:'/Main'
                            })
                            //这是将用户信息保持下来
    //                         let user=resp.data.data
    //                         this.$store.dispatch('setUserAsync',{
    //                             user:user
    //                         });
                        }else{
                            this.$message({
                                message: resp.data.msg,
                                type: 'error'
                            });
                        }
                    }).catch(resp => {
                        this.$message({
                            message: "请求异常",
                            type: 'error'
                        });
                    });
                },
                //更新验证码
                changeVerificationCode(){
                
                    let url = this.axios.urls.VERIFICATION;
                    this.axios.post(url, {}).then(resp => {
                        this.verificationCodeSrc = resp.data;
                    }).catch(resp => {
                        console.log(resp);
                    });
    
                }
            }
            ,
            created() {
                let url = this.axios.urls.VERIFICATION;
                this.axios.post(url, {}).then(resp => {
                    this.verificationCodeSrc = resp.data;
                }).catch(resp => {
                    console.log(resp);
                });
            }
        }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
      .login-wrap {
        box-sizing: border-box;
         100%;
        height: 100%;
        padding-top: 10%;
        background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjEzNjFweCIgaGVpZ2h0PSI2MDlweCIgdmlld0JveD0iMCAwIDEzNjEgNjA5IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCA0Ni4yICg0NDQ5NikgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+R3JvdXAgMjE8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz48L2RlZnM+CiAgICA8ZyBpZD0iQW50LURlc2lnbi1Qcm8tMy4wIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0i6LSm5oi35a+G56CB55m75b2VLeagoemqjCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTc5LjAwMDAwMCwgLTgyLjAwMDAwMCkiPgogICAgICAgICAgICA8ZyBpZD0iR3JvdXAtMjEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDc3LjAwMDAwMCwgNzMuMDAwMDAwKSI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iR3JvdXAtMTgiIG9wYWNpdHk9IjAuOCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzQuOTAxNDE2LCA1NjkuNjk5MTU4KSByb3RhdGUoLTcuMDAwMDAwKSB0cmFuc2xhdGUoLTc0LjkwMTQxNiwgLTU2OS42OTkxNTgpIHRyYW5zbGF0ZSg0LjkwMTQxNiwgNTI1LjE5OTE1OCkiPgogICAgICAgICAgICAgICAgICAgIDxlbGxpcHNlIGlkPSJPdmFsLTExIiBmaWxsPSIjQ0ZEQUU2IiBvcGFjaXR5PSIwLjI1IiBjeD0iNjMuNTc0ODc5MiIgY3k9IjMyLjQ2ODM2NyIgcng9IjIxLjc4MzA0NzkiIHJ5PSIyMS43NjYwMDgiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0zIiBmaWxsPSIjQ0ZEQUU2IiBvcGFjaXR5PSIwLjU5OTk5OTk2NCIgY3g9IjUuOTg3NDY0NzkiIGN5PSIxMy44NjY4NjAxIiByeD0iNS4yMTczOTEzIiByeT0iNS4yMTMzMDk5NyI+PC9lbGxpcHNlPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0zOC4xMzU0NTE0LDg4LjM1MjAyMTUgQzQzLjg5ODQyMjcsODguMzUyMDIxNSA0OC41NzAyMzQsODMuNjgzODY0NyA0OC41NzAyMzQsNzcuOTI1NDAxNSBDNDguNTcwMjM0LDcyLjE2NjkzODMgNDMuODk4NDIyNyw2Ny40OTg3ODE2IDM4LjEzNTQ1MTQsNjcuNDk4NzgxNiBDMzIuMzcyNDgwMSw2Ny40OTg3ODE2IDI3LjcwMDY2ODgsNzIuMTY2OTM4MyAyNy43MDA2Njg4LDc3LjkyNTQwMTUgQzI3LjcwMDY2ODgsODMuNjgzODY0NyAzMi4zNzI0ODAxLDg4LjM1MjAyMTUgMzguMTM1NDUxNCw4OC4zNTIwMjE1IFoiIGlkPSJPdmFsLTMtQ29weSIgZmlsbD0iI0NGREFFNiIgb3BhY2l0eT0iMC40NSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02NC4yNzc1NTgyLDMzLjE3MDQ5NjMgTDExOS4xODU4MzYsMTYuNTY1NDkxNSIgaWQ9IlBhdGgtMTIiIHN0cm9rZT0iI0NGREFFNiIgc3Ryb2tlLXdpZHRoPSIxLjczOTEzMDQzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNDIuMTQzMTcwOCwyNi41MDAyNjgxIEw3LjcxMTkwMTYyLDE0LjU2NDA3MDIiIGlkPSJQYXRoLTE2IiBzdHJva2U9IiNFMEI0QjciIHN0cm9rZS13aWR0aD0iMC43MDI2Nzg5NjQiIG9wYWNpdHk9IjAuNyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtZGFzaGFycmF5PSIxLjQwNTM1Nzg5OTg3MzE1MywyLjEwODAzNjk1MzQ2OTk4MSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02My45MjYyMTg3LDMzLjUyMTU2MSBMNDMuNjcyMTMyNiw2OS4zMjUwOTUxIiBpZD0iUGF0aC0xNSIgc3Ryb2tlPSIjQkFDQUQ5IiBzdHJva2Utd2lkdGg9IjAuNzAyNjc4OTY0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1kYXNoYXJyYXk9IjEuNDA1MzU3ODk5ODczMTUzLDIuMTA4MDM2OTUzNDY5OTgxIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTE3IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjYuODUwOTIyLCAxMy41NDM2NTQpIHJvdGF0ZSgzMC4wMDAwMDApIHRyYW5zbGF0ZSgtMTI2Ljg1MDkyMiwgLTEzLjU0MzY1NCkgdHJhbnNsYXRlKDExNy4yODU3MDUsIDQuMzgxODg5KSIgZmlsbD0iI0NGREFFNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGxpcHNlIGlkPSJPdmFsLTQiIG9wYWNpdHk9IjAuNDUiIGN4PSI5LjEzNDgyNjUzIiBjeT0iOS4xMjc2ODA3NiIgcng9IjkuMTM0ODI2NTMiIHJ5PSI5LjEyNzY4MDc2Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xOC4yNjk2NTMxLDE4LjI1NTM2MTUgQzE4LjI2OTY1MzEsMTMuMjE0MjgyNiAxNC4xNzk4NTE5LDkuMTI3NjgwNzYgOS4xMzQ4MjY1Myw5LjEyNzY4MDc2IEM0LjA4OTgwMTE0LDkuMTI3NjgwNzYgMCwxMy4yMTQyODI2IDAsMTguMjU1MzYxNSBMMTguMjY5NjUzMSwxOC4yNTUzNjE1IFoiIGlkPSJPdmFsLTQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDkuMTM0ODI3LCAxMy42OTE1MjEpIHNjYWxlKC0xLCAtMSkgdHJhbnNsYXRlKC05LjEzNDgyNywgLTEzLjY5MTUyMSkgIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTE0IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMTYuMjk0NzAwLCAxMjMuNzI1NjAwKSByb3RhdGUoLTUuMDAwMDAwKSB0cmFuc2xhdGUoLTIxNi4yOTQ3MDAsIC0xMjMuNzI1NjAwKSB0cmFuc2xhdGUoMTA2LjI5NDcwMCwgMzUuMjI1NjAwKSI+CiAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtMiIgZmlsbD0iI0NGREFFNiIgb3BhY2l0eT0iMC4yNSIgY3g9IjI5LjExNzY0NzEiIGN5PSIyOS4xNDAyNDM5IiByeD0iMjkuMTE3NjQ3MSIgcnk9IjI5LjE0MDI0MzkiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0yIiBmaWxsPSIjQ0ZEQUU2IiBvcGFjaXR5PSIwLjMiIGN4PSIyOS4xMTc2NDcxIiBjeT0iMjkuMTQwMjQzOSIgcng9IjIxLjU2ODYyNzUiIHJ5PSIyMS41ODUzNjU5Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtMi1Db3B5IiBzdHJva2U9IiNDRkRBRTYiIG9wYWNpdHk9IjAuNCIgY3g9IjE3OS4wMTk2MDgiIGN5PSIxMzguMTQ2MzQxIiByeD0iMjMuNzI1NDkwMiIgcnk9IjIzLjc0MzkwMjQiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0yIiBmaWxsPSIjQkFDQUQ5IiBvcGFjaXR5PSIwLjUiIGN4PSIyOS4xMTc2NDcxIiBjeT0iMjkuMTQwMjQzOSIgcng9IjEwLjc4NDMxMzciIHJ5PSIxMC43OTI2ODI5Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI5LjExNzY0NzEsMzkuOTMyOTI2OCBMMjkuMTE3NjQ3MSwxOC4zNDc1NjEgQzIzLjE2MTYzNTEsMTguMzQ3NTYxIDE4LjMzMzMzMzMsMjMuMTc5NjA5NyAxOC4zMzMzMzMzLDI5LjE0MDI0MzkgQzE4LjMzMzMzMzMsMzUuMTAwODc4MSAyMy4xNjE2MzUxLDM5LjkzMjkyNjggMjkuMTE3NjQ3MSwzOS45MzI5MjY4IFoiIGlkPSJPdmFsLTIiIGZpbGw9IiNCQUNBRDkiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8ZyBpZD0iR3JvdXAtOSIgb3BhY2l0eT0iMC40NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTcyLjAwMDAwMCwgMTMxLjAwMDAwMCkiIGZpbGw9IiNFNkExQTYiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0yLUNvcHktMiIgY3g9IjcuMDE5NjA3ODQiIGN5PSI3LjE0NjM0MTQ2IiByeD0iNi40NzA1ODgyNCIgcnk9IjYuNDc1NjA5NzYiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTAuNTQ5MDE5NjA4LDEzLjYyMTk1MTIgQzQuMTIyNjI2ODEsMTMuNjIxOTUxMiA3LjAxOTYwNzg0LDEwLjcyMjcyMiA3LjAxOTYwNzg0LDcuMTQ2MzQxNDYgQzcuMDE5NjA3ODQsMy41Njk5NjA5NSA0LjEyMjYyNjgxLDAuNjcwNzMxNzA3IDAuNTQ5MDE5NjA4LDAuNjcwNzMxNzA3IEwwLjU0OTAxOTYwOCwxMy42MjE5NTEyIFoiIGlkPSJPdmFsLTItQ29weS0yIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzLjc4NDMxNCwgNy4xNDYzNDEpIHNjYWxlKC0xLCAxKSB0cmFuc2xhdGUoLTMuNzg0MzE0LCAtNy4xNDYzNDEpICI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0xMCIgZmlsbD0iI0NGREFFNiIgY3g9IjIxOC4zODIzNTMiIGN5PSIxMzguNjg1OTc2IiByeD0iMS42MTc2NDcwNiIgcnk9IjEuNjE4OTAyNDQiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0xMC1Db3B5LTIiIGZpbGw9IiNFMEI0QjciIG9wYWNpdHk9IjAuMzUiIGN4PSIxNzkuNTU4ODI0IiBjeT0iMTc1LjM4MTA5OCIgcng9IjEuNjE3NjQ3MDYiIHJ5PSIxLjYxODkwMjQ0Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtMTAtQ29weSIgZmlsbD0iI0UwQjRCNyIgb3BhY2l0eT0iMC4zNSIgY3g9IjE4MC4wOTgwMzkiIGN5PSIxMDIuNTMwNDg4IiByeD0iMi4xNTY4NjI3NSIgcnk9IjIuMTU4NTM2NTkiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMjguOTk4NTM4MSwyOS45NjcxNTk4IEwxNzEuMTUxMDE4LDEzMi44NzYwMjQiIGlkPSJQYXRoLTExIiBzdHJva2U9IiNDRkRBRTYiIG9wYWNpdHk9IjAuOCI+PC9wYXRoPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTEwIiBvcGFjaXR5PSIwLjc5OTk5OTk1MiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTA1NC4xMDA2MzUsIDM2LjY1OTMxNykgcm90YXRlKC0xMS4wMDAwMDApIHRyYW5zbGF0ZSgtMTA1NC4xMDA2MzUsIC0zNi42NTkzMTcpIHRyYW5zbGF0ZSgxMDI2LjYwMDYzNSwgNC42NTkzMTcpIj4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC03IiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMC45NDExNzY0NzEiIGN4PSI0My44MTM1NTkzIiBjeT0iMzIiIHJ4PSIxMS4xODY0NDA3IiByeT0iMTEuMjk0MTE3NiI+PC9lbGxpcHNlPgogICAgICAgICAgICAgICAgICAgIDxnIGlkPSJHcm91cC0xMiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzQuNTk2Nzc0LCAyMy4xMTExMTEpIiBmaWxsPSIjQkFDQUQ5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtNyIgb3BhY2l0eT0iMC40NSIgY3g9IjkuMTg1MzQ3MTgiIGN5PSI4Ljg4ODg4ODg5IiByeD0iOC40NzQ1NzYyNyIgcnk9IjguNTU2MTQ5NzMiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkuMTg1MzQ3MTgsMTcuNDQ1MDM4NiBDMTMuODY1NzI2NCwxNy40NDUwMzg2IDE3LjY1OTkyMzUsMTMuNjE0MzE5OSAxNy42NTk5MjM1LDguODg4ODg4ODkgQzE3LjY1OTkyMzUsNC4xNjM0NTc4NyAxMy44NjU3MjY0LDAuMzMyNzM5MTU2IDkuMTg1MzQ3MTgsMC4zMzI3MzkxNTYgTDkuMTg1MzQ3MTgsMTcuNDQ1MDM4NiBaIiBpZD0iT3ZhbC03Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0zNC42NTk3Mzg1LDI0LjgwOTY5NCBMNS43MTY2NjA4NCw0Ljc2ODc4OTQ1IiBpZD0iUGF0aC0yIiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMC45NDExNzY0NzEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbCIgc3Ryb2tlPSIjQ0ZEQUU2IiBzdHJva2Utd2lkdGg9IjAuOTQxMTc2NDcxIiBjeD0iMy4yNjI3MTE4NiIgY3k9IjMuMjk0MTE3NjUiIHJ4PSIzLjI2MjcxMTg2IiByeT0iMy4yOTQxMTc2NSI+PC9lbGxpcHNlPgogICAgICAgICAgICAgICAgICAgIDxlbGxpcHNlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiNGN0UxQUQiIGN4PSIyLjc5NjYxMDE3IiBjeT0iNjEuMTc2NDcwNiIgcng9IjIuNzk2NjEwMTciIHJ5PSIyLjgyMzUyOTQxIj48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTM0LjYzMTI0NDMsMzkuMjkyMjcxMiBMNS4wNjM2NjY2Myw1OS43ODUwODIiIGlkPSJQYXRoLTEwIiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMC45NDExNzY0NzEiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxnIGlkPSJHcm91cC0xOSIgb3BhY2l0eT0iMC4zMyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI4Mi41MzcyMTksIDQ0Ni41MDI4NjcpIHJvdGF0ZSgtMTAuMDAwMDAwKSB0cmFuc2xhdGUoLTEyODIuNTM3MjE5LCAtNDQ2LjUwMjg2NykgdHJhbnNsYXRlKDExNDIuNTM3MjE5LCAzMjcuNTAyODY3KSI+CiAgICAgICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTE3IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDEuMzMzNTM5LCAxMDQuNTAyNzQyKSByb3RhdGUoMjc1LjAwMDAwMCkgdHJhbnNsYXRlKC0xNDEuMzMzNTM5LCAtMTA0LjUwMjc0MikgdHJhbnNsYXRlKDEyOS4zMzM1MzksIDkyLjUwMjc0MikiIGZpbGw9IiNCQUNBRDkiPgogICAgICAgICAgICAgICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLTQiIG9wYWNpdHk9IjAuNDUiIGN4PSIxMS42NjY2NjY3IiBjeT0iMTEuNjY2NjY2NyIgcj0iMTEuNjY2NjY2NyI+PC9jaXJjbGU+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMy4zMzMzMzMzLDIzLjMzMzMzMzMgQzIzLjMzMzMzMzMsMTYuODkwMDExMyAxOC4xMDk5ODg3LDExLjY2NjY2NjcgMTEuNjY2NjY2NywxMS42NjY2NjY3IEM1LjIyMzM0NDU5LDExLjY2NjY2NjcgMCwxNi44OTAwMTEzIDAsMjMuMzMzMzMzMyBMMjMuMzMzMzMzMywyMy4zMzMzMzMzIFoiIGlkPSJPdmFsLTQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDExLjY2NjY2NywgMTcuNTAwMDAwKSBzY2FsZSgtMSwgLTEpIHRyYW5zbGF0ZSgtMTEuNjY2NjY3LCAtMTcuNTAwMDAwKSAiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC01LUNvcHktNiIgZmlsbD0iI0NGREFFNiIgY3g9IjIwMS44MzMzMzMiIGN5PSI4Ny41IiByPSI1LjgzMzMzMzMzIj48L2NpcmNsZT4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQzLjUsODguODEyNjY4NSBMMTU1LjA3MDUwMSwxNy42MDM4NTQ0IiBpZD0iUGF0aC0xNyIgc3Ryb2tlPSIjQkFDQUQ5IiBzdHJva2Utd2lkdGg9IjEuMTY2NjY2NjciPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTcuNSwzNy4zMzMzMzMzIEwxMjcuNDY2MjUyLDk3LjY0NDk3MzUiIGlkPSJQYXRoLTE4IiBzdHJva2U9IiNCQUNBRDkiIHN0cm9rZS13aWR0aD0iMS4xNjY2NjY2NyI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwb2x5bGluZSBpZD0iUGF0aC0xOSIgc3Ryb2tlPSIjQ0ZEQUU2IiBzdHJva2Utd2lkdGg9IjEuMTY2NjY2NjciIHBvaW50cz0iMTQzLjkwMjU5NyAxMjAuMzAyMjgxIDE3NC45MzU0NTUgMjMxLjU3MTM0MiAzOC41IDE0Ny41MTA4NDcgMTI2LjM2Njk0MSAxMTAuODMzMzMzIj48L3BvbHlsaW5lPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTkuODMzMzMzLDk5Ljc0NTM4NDIgTDE5NS40MTY2NjcsODkuMjUiIGlkPSJQYXRoLTIwIiBzdHJva2U9IiNFMEI0QjciIHN0cm9rZS13aWR0aD0iMS4xNjY2NjY2NyIgb3BhY2l0eT0iMC42Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIwNS4zMzMzMzMsODIuMTM3MjEwNSBMMjM4LjcxOTQwNiwzNi4xNjY2NjY3IiBpZD0iUGF0aC0yNCIgc3Ryb2tlPSIjQkFDQUQ5IiBzdHJva2Utd2lkdGg9IjEuMTY2NjY2NjciPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMjY2LjcyMzQyNCwxMzIuMjMxOTg4IEwyMDcuMDgzMzMzLDkwLjQxNjY2NjciIGlkPSJQYXRoLTI1IiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMS4xNjY2NjY2NyI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwtNSIgZmlsbD0iI0MxRDFFMCIgY3g9IjE1Ni45MTY2NjciIGN5PSI4Ljc1IiByPSI4Ljc1Ij48L2NpcmNsZT4KICAgICAgICAgICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLTUtQ29weS0zIiBmaWxsPSIjQzFEMUUwIiBjeD0iMzkuMDgzMzMzMyIgY3k9IjE0OC43NSIgcj0iNS4yNSI+PC9jaXJjbGU+CiAgICAgICAgICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC01LUNvcHktMiIgZmlsbC1vcGFjaXR5PSIwLjYiIGZpbGw9IiNEMURFRUQiIGN4PSI4Ljc1IiBjeT0iMzMuMjUiIHI9IjguNzUiPjwvY2lyY2xlPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwtNS1Db3B5LTQiIGZpbGwtb3BhY2l0eT0iMC42IiBmaWxsPSIjRDFERUVEIiBjeD0iMjQzLjgzMzMzMyIgY3k9IjMwLjMzMzMzMzMiIHI9IjUuODMzMzMzMzMiPjwvY2lyY2xlPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwtNS1Db3B5LTUiIGZpbGw9IiNFMEI0QjciIGN4PSIxNzUuNTgzMzMzIiBjeT0iMjMyLjc1IiByPSI1LjI1Ij48L2NpcmNsZT4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+);
        /* background-color: #112346; */
        background-repeat: no-repeat;
        background-position: center right;
        background-size: 100%;
      }
    
      .login-container {
        border-radius: 10px;
        margin: 0px auto;
         350px;
        padding: 30px 35px 15px 35px;
        background: #fff;
        border: 1px solid #eaeaea;
        text-align: left;
        box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.1);
      }
    
      .title {
        margin: 0px auto 40px auto;
        text-align: center;
        color: #505458;
      }
    </style>

    到这里结束了

  • 相关阅读:
    E. Gosha is hunting (dp + wqs套wqs)
    【Codeforces Round #575 (Div. 3) 】 RGB Substring (hard version) ( FFT)
    C
    poj 1160 Post Office(dp + wqs二分)
    【 2018南京 】Magic Potion (网络流)
    【 2018南京 】Kangaroo Puzzle (思维+暴力模拟)
    【 2018南京 】Country Meow (模拟退火)
    【2018焦作网络赛】 Jiu Yuan Wants to Eat (熟练剖分 + 思维)
    【2018焦作网络赛】 Modular Production Line(费用流)
    【2018焦作网络赛】 B. Mathematical Curse (dp)
  • 原文地址:https://www.cnblogs.com/ztbk/p/11776052.html
Copyright © 2020-2023  润新知