zoukankan      html  css  js  c++  java
  • springmvc文件上传AND jwt身份验证

    SpringMVC文件上传

    思路:
    1、首先定义页面,定义多功能表单(enctype=“multipart/form-data”)
    2、在Controller里面定义一个方法,用参数(MultipartFile)来接收前台传递过来的文件对象
    3、然后文件上传就是把文件从一个地方(本地)复制到另外一个地方(服务器)

    添加pom依赖

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

    配置springservlet-mvc.xml

        <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>

    文件上传的方法

     /**
         * 文件上传的方法
         * @param req
         * @return
         */
        @RequestMapping("/upload")
        public String upload(HttpServletRequest req, MultipartFile xxx){
            String fileName = xxx.getOriginalFilename();
            String contentType = xxx.getContentType();
            try {
                FileUtils.copyInputStreamToFile(xxx.getInputStream(),new File("D:/xxx/"+fileName));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "redirect:/book/list";
        }

    upload.jsp

    <%--
      Created by IntelliJ IDEA.
      User: 源
      Date: 2019/10/30
      Time: 18:12
      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="/book/upload" method="post" enctype="multipart/form-data">
        请选择文件:<input type="file" name="xxx" />
        <input type="submit" value="ok" />
    
    </form>
    </body>
    </html>

    上传结果

    通过浏览器进行访问

    Jwt实现登录验证

    思路:
    登录界面向后台请求验证码,后台就先调用随机函数生成验证码,并且根据验证码生成一张图片,以 base64 字符串的形式传到前台,这时我们还要生成verificationJwt令牌做为请求验证码客户端的区分。我们先将验证码信息存入redis。key是 verificationJwt令牌的值,value就是验证码了。并且将令牌放入到响应头。传给客户端。当客户端提交的时候将保持的verificationJwt令牌放入请求头带过来。后端根据前端传过来的 jwt令牌去redis中获取数据,将验证码拿到后和现有的验证码进行比较。看看是否相等
    细节:
    访问一次登录页面,生成一个verificationJwt令牌,这个令牌的有效时间是5min,验证码在redis中的有效时间是1min,无论是verificationJwt令牌超时失效,还是验证码生成后超时失效,都会造成登录失败;
    简单来说就是跳转到登陆界面,需要在5min中之内完成登录,新的验证码出来后,需要在1min中之内完成登录

    导入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>

    vue前端代码

    State.js

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

    Mutations.js

    setVerificationJwt: (state, payload) => {
              state.verificationJwt = payload.verificationJwt;
          },

    Getters.js

    getVerificationJwt:(state) =>{
              return state.verificationJwt;
          },

    action.js

    /**
     * 对后台请求的地址的封装,URL格式如下:
     * 模块名_实体名_操作
     */
    export default {
        // 'SERVER': 'http://localhost:8080/T216_SSH', //服务器
      'SERVER': 'http://localhost:8080', // /webDemo/ssm 服务器
      'SYSTEM_USER_DOLOGIN': '/vue/user/login', //用户登陆
      'VERIFICATION': '/vue/user/verificationCode', //用户登陆
        // 'SYSTEM_USER_DOLOGIN': '/vue/userAction_login.action', //用户登陆
        // '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

     1 // 请求拦截器
     2 axios.interceptors.request.use(function(config) {
     3     //设置验证码jwt令牌
     4     let verificationJwt = window.vm.$store.getters.getVerificationJwt;
     5     if (verificationJwt) {
     6         config.headers['verificationJwt'] = verificationJwt;
     7     }
     8 
     9     var jwt = window.vm.$store.getters.getJwt;
    10     config.headers['jwt'] = jwt;
    11     return config;
    12 }, function(error) {
    13     return Promise.reject(error);
    14 });
    15 
    16 // 响应拦截器
    17 axios.interceptors.response.use(function(response) {
    18     // debugger;
    19     //保存验证码jwt令牌
    20     let verificationjwt = response.headers['verificationjwt'];
    21     if (verificationjwt) {
    22         window.vm.$store.commit('setVerificationJwt', {
    23             verificationJwt: verificationjwt
    24         });
    25     }
    26     
    27     var jwt = response.headers['jwt'];
    28     if (jwt) {
    29         window.vm.$store.commit('setJwt', {
    30             jwt: jwt
    31         });
    32     }
    33     return response;
    34 }, function(error) {
    35     return Promise.reject(error);
    36 });

    登录界面login.vue

      1 <template>
      2   <div class="login-wrap">
      3     <el-form class="login-container">
      4       <h1 class="title">用户登录</h1>
      5       <el-form-item label="">
      6         <el-input type="text" v-model="userName" placeholder="请输入登录账号" autocomplete="off"></el-input>
      7       </el-form-item>
      8       <el-form-item label="">
      9         <el-input type="password" v-model="userPwd" placeholder="请输入登录密码" autocomplete="off"></el-input>
     10       </el-form-item>
     11       <el-form-item label="">
     12         <el-row>
     13           <el-col :span="16">
     14             <el-input type="text" v-model="verificationCode" placeholder="请输入验证码" autocomplete="off"></el-input>
     15           </el-col>
     16           <el-col :span="8">
     17             <img id="img" :src="verificationCodeSrc" width="116px" height="40px" @click="changeVerificationCode" >
     18           </el-col>
     19         </el-row>
     20       </el-form-item>
     21       <el-form-item>
     22         <el-button type="primary" style=" 100%;" @click="doSubmit">登  录</el-button>
     23       </el-form-item>
     24       <el-row style="text-align: center; margin-top: -15;">
     25         <el-link type="primary">忘记密码</el-link>
     26         <el-link type="primary" @click="gotoRegister">用户注册</el-link>
     27       </el-row>
     28     </el-form>
     29   </div>
     30 </template>
     31 
     32 
     33 <script>
     34     export default {
     35         name: 'Login',
     36         data: function() {
     37             return {
     38                 userName: null,
     39                 userPwd: null,
     40                 verificationCode:null,
     41                 verificationCodeSrc:null
     42             }
     43         },
     44         methods: {
     45             gotoRegister:function(){
     46                 this.$router.push('/Register');
     47             },
     48             doSubmit: function() {
     49                 let params = {
     50                     uname: this.userName,
     51                     pwd: this.userPwd,
     52                     verificationCode: this.verificationCode
     53                 };
     54                 let url = this.axios.urls.SYSTEM_USER_DOLOGIN;
     55 
     56                 this.axios.post(url, params).then(resp => {
     57                     if(resp.data.status==200) {
     58                         //提示登录成功
     59                         this.$message({
     60                             message: resp.data.msg,
     61                             type: 'success'
     62                         });
     63                         //跳转路由
     64                         this.$router.push({
     65                             path:'/Main'
     66                         })
     67                         //这是将用户信息保持下来
     68 //                         let user=resp.data.data
     69 //                         this.$store.dispatch('setUserAsync',{
     70 //                             user:user
     71 //                         });
     72                     }else{
     73                         this.$message({
     74                             message: resp.data.msg,
     75                             type: 'error'
     76                         });
     77                     }
     78                 }).catch(resp => {
     79                     this.$message({
     80                         message: "请求异常",
     81                         type: 'error'
     82                     });
     83                 });
     84             },
     85             //更新验证码
     86             changeVerificationCode(){
     87                 let url = this.axios.urls.VERIFICATION;
     88                 this.axios.post(url, {}).then(resp => {
     89                     this.verificationCodeSrc = resp.data;
     90                 }).catch(resp => {
     91                     console.log(resp);
     92                 });
     93 
     94             }
     95         }
     96         ,
     97         created() {
     98             let url = this.axios.urls.VERIFICATION;
     99             this.axios.post(url, {}).then(resp => {
    100                 this.verificationCodeSrc = resp.data;
    101             }).catch(resp => {
    102                 console.log(resp);
    103             });
    104         }
    105     }
    106 </script>
    107 
    108 <!-- Add "scoped" attribute to limit CSS to this component only -->
    109 <style scoped>
    110   .login-wrap {
    111     box-sizing: border-box;
    112      100%;
    113     height: 100%;
    114     padding-top: 10%;
    115     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+);
    116     /* background-color: #112346; */
    117     background-repeat: no-repeat;
    118     background-position: center right;
    119     background-size: 100%;
    120   }
    121 
    122   .login-container {
    123     border-radius: 10px;
    124     margin: 0px auto;
    125      350px;
    126     padding: 30px 35px 15px 35px;
    127     background: #fff;
    128     border: 1px solid #eaeaea;
    129     text-align: left;
    130     box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.1);
    131   }
    132 
    133   .title {
    134     margin: 0px auto 40px auto;
    135     text-align: center;
    136     color: #505458;
    137   }
    138 </style>

    后端代码

    Util类

    CorsFilter

     1 package com.yuan.util;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.Filter;
     6 import javax.servlet.FilterChain;
     7 import javax.servlet.FilterConfig;
     8 import javax.servlet.ServletException;
     9 import javax.servlet.ServletRequest;
    10 import javax.servlet.ServletResponse;
    11 import javax.servlet.http.HttpServletRequest;
    12 import javax.servlet.http.HttpServletResponse;
    13 
    14 /**
    15  * 配置tomcat允许跨域访问
    16  *
    17  * @author Administrator
    18  *
    19  */
    20 public class CorsFilter implements Filter {
    21 
    22     @Override
    23     public void init(FilterConfig filterConfig) throws ServletException {
    24     }
    25 
    26     @Override
    27     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
    28             throws IOException, ServletException {
    29         HttpServletResponse resp = (HttpServletResponse) servletResponse;
    30         HttpServletRequest req = (HttpServletRequest) servletRequest;
    31 
    32         // Access-Control-Allow-Origin就是我们需要设置的域名
    33         // Access-Control-Allow-Headers跨域允许包含的头。
    34         // Access-Control-Allow-Methods是允许的请求方式
    35         resp.setHeader("Access-Control-Allow-Origin", "*");// *,任何域名
    36         resp.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE");
    37         // resp.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With,
    38         // Content-Type, Accept");
    39         // 允许客户端,发一个新的请求头jwt
    40         //允许客户端发送一个新的请求头
    41         resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, jwt, verificationJwt");
    42         //允许客户端处理一个新的响应头jwt
    43         resp.setHeader("Access-Control-Expose-Headers", "jwt");
    44         resp.setHeader("Access-Control-Expose-Headers", "verificationJwt");
    45         // String sss = resp.getHeader("Access-Control-Expose-Headers");
    46         // System.out.println("sss=" + sss);
    47 
    48         // 允许请求头Token
    49         // httpResponse.setHeader("Access-Control-Allow-Headers","Origin,X-Requested-With,
    50         // Content-Type, Accept, Token");
    51         // System.out.println("Token=" + req.getHeader("Token"));
    52 
    53         if ("OPTIONS".equals(req.getMethod())) {// axios的ajax会发两次请求,第一次提交方式为:option,直接返回即可
    54             return;
    55         }
    56         filterChain.doFilter(servletRequest, servletResponse);
    57     }
    58 
    59     @Override
    60     public void destroy() {
    61 
    62     }
    63 }

    ImageUtil

     1 package com.yuan.util;
     2 
     3 import sun.misc.BASE64Encoder;
     4 
     5 import javax.imageio.ImageIO;
     6 import java.awt.*;
     7 import java.awt.image.BufferedImage;
     8 import java.io.ByteArrayOutputStream;
     9 import java.io.IOException;
    10 import java.util.Random;
    11 
    12 public class ImageUtil {
    13 
    14     /**
    15      * 根据指定的随机数 生成验证码图片 转 base64
    16      * @param word 要生存的验证码随机字符串
    17      * @param width 图片宽度
    18      * @param height 图片高度
    19      * @return base64 格式生成的验证码图片
    20      * @throws IOException
    21      */
    22     public static String createImageWithVerifyCode(String word, int width, int height) throws IOException {
    23         String png_base64="";
    24         //绘制内存中的图片
    25         BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
    26         //得到画图对象
    27         Graphics graphics = bufferedImage.getGraphics();
    28         //绘制图片前指定一个颜色
    29         graphics.setColor(getRandColor(160,200));
    30         graphics.fillRect(0,0,width,height);
    31         //绘制边框
    32         graphics.setColor(Color.white);
    33         graphics.drawRect(0, 0, width - 1, height - 1);
    34         // 步骤四 四个随机数字
    35         Graphics2D graphics2d = (Graphics2D) graphics;
    36         graphics2d.setFont(new Font("宋体", Font.BOLD, 18));
    37         Random random = new Random();
    38         // 定义x坐标
    39         int x = 10;
    40         for (int i = 0; i < word.length(); i++) {
    41             // 随机颜色
    42             graphics2d.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
    43             // 旋转 -30 --- 30度
    44             int jiaodu = random.nextInt(60) - 30;
    45             // 换算弧度
    46             double theta = jiaodu * Math.PI / 180;
    47             // 获得字母数字
    48             char c = word.charAt(i);
    49             //将c 输出到图片
    50             graphics2d.rotate(theta, x, 20);
    51             graphics2d.drawString(String.valueOf(c), x, 20);
    52             graphics2d.rotate(-theta, x, 20);
    53             x += 30;
    54         }
    55         // 绘制干扰线
    56         graphics.setColor(getRandColor(160, 200));
    57         int x1;
    58         int x2;
    59         int y1;
    60         int y2;
    61         for (int i = 0; i < 30; i++) {
    62             x1 = random.nextInt(width);
    63             x2 = random.nextInt(12);
    64             y1 = random.nextInt(height);
    65             y2 = random.nextInt(12);
    66             graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
    67         }
    68         graphics.dispose();// 释放资源
    69         ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
    70         ImageIO.write(bufferedImage, "png", baos);//写入流中
    71         byte[] bytes = baos.toByteArray();//转换成字节
    72         BASE64Encoder encoder = new BASE64Encoder();
    73         png_base64 = encoder.encodeBuffer(bytes).trim();
    74         png_base64 = png_base64.replaceAll("
    ", "").replaceAll("
    ", "");//删除 
    
    75         return png_base64;
    76     }
    77 
    78 
    79 
    80     /**设置随机颜色*/
    81     private static Color getRandColor(int fc, int bc) {
    82         // 取其随机颜色
    83         Random random = new Random();
    84         if (fc > 255) {
    85             fc = 255;
    86         }
    87         if (bc > 255) {
    88             bc = 255;
    89         }
    90         int r = fc + random.nextInt(bc - fc);
    91         int g = fc + random.nextInt(bc - fc);
    92         int b = fc + random.nextInt(bc - fc);
    93         return new Color(r, g, b);
    94     }
    95 
    96 }

    JSONResult

     1 package com.yuan.util;
     2 
     3 public class JSONResult {
     4 
     5     // 响应业务状态
     6     private Integer status;
     7 
     8     // 响应消息
     9     private String msg;
    10 
    11     // 响应中的数据
    12     private Object data;
    13     
    14     private String ok; // 不使用
    15 
    16     public static JSONResult build(Integer status, String msg, Object data) {
    17         return new JSONResult(status, msg, data);
    18     }
    19 
    20     public static JSONResult ok(Object data) {
    21         return new JSONResult(data);
    22     }
    23 
    24     public static JSONResult ok() {
    25         return new JSONResult(null);
    26     }
    27     
    28     public static JSONResult errorMsg(String msg) {
    29         return new JSONResult(500, msg, null);
    30     }
    31     
    32     public static JSONResult errorMap(Object data) {
    33         return new JSONResult(501, "error", data);
    34     }
    35     
    36     public static JSONResult errorTokenMsg(String msg) {
    37         return new JSONResult(502, msg, null);
    38     }
    39     
    40     public static JSONResult errorException(String msg) {
    41         return new JSONResult(555, msg, null);
    42     }
    43 
    44     public JSONResult() {
    45 
    46     }
    47 
    48     public JSONResult(Integer status, String msg, Object data) {
    49         this.status = status;
    50         this.msg = msg;
    51         this.data = data;
    52     }
    53 
    54     public JSONResult(Object data) {
    55         this.status = 200;
    56         this.msg = "OK";
    57         this.data = data;
    58     }
    59 
    60     public Boolean isOK() {
    61         return this.status == 200;
    62     }
    63 
    64     public Integer getStatus() {
    65         return status;
    66     }
    67 
    68     public void setStatus(Integer status) {
    69         this.status = status;
    70     }
    71 
    72     public String getMsg() {
    73         return msg;
    74     }
    75 
    76     public void setMsg(String msg) {
    77         this.msg = msg;
    78     }
    79 
    80     public Object getData() {
    81         return data;
    82     }
    83 
    84     public void setData(Object data) {
    85         this.data = data;
    86     }
    87 
    88    public String getOk() {
    89       return ok;
    90    }
    91 
    92    public void setOk(String ok) {
    93       this.ok = ok;
    94    }
    95 
    96 }

    JwtUtils

      1 package com.yuan.util;
      2 
      3 import java.util.Date;
      4 import java.util.Map;
      5 import java.util.UUID;
      6 
      7 import javax.crypto.SecretKey;
      8 import javax.crypto.spec.SecretKeySpec;
      9 
     10 import org.apache.commons.codec.binary.Base64;
     11 
     12 import io.jsonwebtoken.Claims;
     13 import io.jsonwebtoken.JwtBuilder;
     14 import io.jsonwebtoken.Jwts;
     15 import io.jsonwebtoken.SignatureAlgorithm;
     16 
     17 /**
     18  * JWT验证过滤器:配置顺序 CorsFilte->JwtUtilsr-->StrutsPrepareAndExecuteFilter
     19  *
     20  */
     21 public class JwtUtils {
     22    /**
     23     * JWT_WEB_TTL:WEBAPP应用中token的有效时间,默认30分钟
     24     */
     25    public static final long JWT_WEB_TTL = 5 * 60 * 1000;
     26 
     27    /**
     28     * 将jwt令牌保存到header中的key
     29     */
     30    public static final String JWT_HEADER_KEY = "jwt";
     31    public static final String JWT_VERIFICATION_KEY = "verificationJwt";
     32 
     33    // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
     34    private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
     35    private static final String JWT_SECRET = "f356cdce935c42328ad2001d7e9552a3";// JWT密匙
     36    private static final SecretKey JWT_KEY;// 使用JWT密匙生成的加密key
     37 // private static final SecretKey JWT_VERIFICATION_KEY;// 使用JWT密匙生成的加密key
     38 
     39 
     40    static {
     41       byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);
     42       JWT_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
     43 //    这里我偷个懒,用户登录jwt密钥,与图形验证码jwt密钥搞成同一个
     44 //    JWT_VERIFICATION_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
     45    }
     46 
     47    private JwtUtils() {
     48    }
     49 
     50    /**
     51     * 解密jwt,获得所有声明(包括标准和私有声明)
     52     * 
     53     * @param jwt
     54     * @return
     55     * @throws Exception
     56     */
     57    public static Claims parseJwt(String jwt) {
     58       Claims claims = Jwts.parser().setSigningKey(JWT_KEY).parseClaimsJws(jwt).getBody();
     59       return claims;
     60    }
     61 
     62    /**
     63     * 创建JWT令牌,签发时间为当前时间
     64     * 
     65     * @param claims
     66     *            创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
     67     * @param ttlMillis
     68     *            JWT的有效时间(单位毫秒),当前时间+有效时间=过期时间
     69     * @return jwt令牌
     70     */
     71    public static String createJwt(Map<String, Object> claims, long ttlMillis) {
     72       // 生成JWT的时间,即签发时间
     73       long nowMillis = System.currentTimeMillis();
     74 
     75       // 下面就是在为payload添加各种标准声明和私有声明了
     76       // 这里其实就是new一个JwtBuilder,设置jwt的body
     77       JwtBuilder builder = Jwts.builder()
     78             // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
     79             .setClaims(claims)
     80             // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
     81             // 可以在未登陆前作为身份标识使用
     82             .setId(UUID.randomUUID().toString().replace("-", ""))
     83             // iss(Issuser)签发者,写死
     84             // .setIssuer("zking")
     85             // iat: jwt的签发时间
     86             .setIssuedAt(new Date(nowMillis))
     87             // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
     88             // .setSubject("{}")
     89             // 设置签名使用的签名算法和签名使用的秘钥
     90             .signWith(SIGNATURE_ALGORITHM, JWT_KEY)
     91             // 设置JWT的过期时间
     92             .setExpiration(new Date(nowMillis + ttlMillis));
     93 
     94       return builder.compact();
     95    }
     96 
     97    /**
     98     * 复制jwt,并重新设置签发时间(为当前时间)和失效时间
     99     * 
    100     * @param jwt
    101     *            被复制的jwt令牌
    102     * @param ttlMillis
    103     *            jwt的有效时间(单位毫秒),当前时间+有效时间=过期时间
    104     * @return
    105     */
    106    public static String copyJwt(String jwt, Long ttlMillis) {
    107       Claims claims = parseJwt(jwt);
    108 
    109       // 生成JWT的时间,即签发时间
    110       long nowMillis = System.currentTimeMillis();
    111 
    112       // 下面就是在为payload添加各种标准声明和私有声明了
    113       // 这里其实就是new一个JwtBuilder,设置jwt的body
    114       JwtBuilder builder = Jwts.builder()
    115             // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
    116             .setClaims(claims)
    117             // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
    118             // 可以在未登陆前作为身份标识使用
    119             //.setId(UUID.randomUUID().toString().replace("-", ""))
    120             // iss(Issuser)签发者,写死
    121             // .setIssuer("zking")
    122             // iat: jwt的签发时间
    123             .setIssuedAt(new Date(nowMillis))
    124             // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
    125             // .setSubject("{}")
    126             // 设置签名使用的签名算法和签名使用的秘钥
    127             .signWith(SIGNATURE_ALGORITHM, JWT_KEY)
    128             // 设置JWT的过期时间
    129             .setExpiration(new Date(nowMillis + ttlMillis));
    130       return builder.compact();
    131    }
    132 
    133    public static Claims validateJwtToken(String jwt) {
    134       Claims claims = null;
    135       try {
    136          if (null != jwt) {
    137             claims = JwtUtils.parseJwt(jwt);
    138          }
    139       } catch (Exception e) {
    140          e.printStackTrace();
    141       }
    142       return claims;
    143    }
    144 }

    VerifyCodeUtil

     1 package com.yuan.util;
     2 
     3 import java.util.Random;
     4 
     5 public class VerifyCodeUtil {
     6 
     7 
     8     /**生成N位数字和字母混合的验证码
     9      * @param  num 验证码位数
    10      * @return code 生成的验证码字符串*/
    11     public static String produceNumAndChar(int num){
    12         Random random = new Random();
    13         String code = "";
    14         String ch = "ABCDEFGHIJKLMNPQRSTUVWXYZ";
    15         String n = "123456789";
    16         for(int i=0;i<num;i++){
    17             int flag = random.nextInt(2);
    18             if(flag==0){//数字
    19                 code+=n.charAt(random.nextInt(n.length()));
    20             }else{//字母
    21                 code+=ch.charAt(random.nextInt(ch.length()));
    22             }
    23         }
    24         return code;
    25     }
    26 }

    实体类User

     1 package com.yuan.model;
     2 
     3 public class User {
     4     private String uname;
     5 
     6     private String pwd;
     7 
     8     public User(String uname, String pwd) {
     9         this.uname = uname;
    10         this.pwd = pwd;
    11     }
    12 
    13     public User() {
    14         super();
    15     }
    16 
    17     public String getUname() {
    18         return uname;
    19     }
    20 
    21     public void setUname(String uname) {
    22         this.uname = uname;
    23     }
    24 
    25     public String getPwd() {
    26         return pwd;
    27     }
    28 
    29     public void setPwd(String pwd) {
    30         this.pwd = pwd;
    31     }
    32 }

    UserMapper.xml

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
     3 <mapper namespace="com.yuan.mapper.UserMapper">
     4 
     5   <select id="login" parameterType="java.lang.String" >
     6     select 
     7     *
     8     from t_vue_user
     9     where uname = #{uname,jdbcType=VARCHAR}
    10   </select>
    11 
    12 </mapper>

    UserMapper

    1 package com.yuan.mapper;
    2 
    3 import com.yuan.model.User;
    4 import org.springframework.stereotype.Repository;
    5 
    6 @Repository
    7 public interface UserMapper {
    8     User login(User user);
    9 }

    UserService

    package com.yuan.service;
    
    import com.yuan.model.User;
    
    public interface UserService {
    
            public User login(User user);
    }

    UserServiceImpl

     1 package com.yuan.service.impl;
     2 
     3 import com.yuan.mapper.UserMapper;
     4 import com.yuan.model.User;
     5 import com.yuan.service.UserService;
     6 import org.springframework.beans.factory.annotation.Autowired;
     7 import org.springframework.stereotype.Service;
     8 
     9 @Service
    10 public class UserServiceImpl implements UserService {
    11 
    12 
    13     @Autowired
    14     private UserMapper userMapper;
    15 
    16 
    17     @Override
    18     public User login(User user) {
    19         return userMapper.login(user);
    20     }
    21 }

    UserController

     1 package com.yuan.controller;
     2 
     3 import com.yuan.model.User;
     4 import com.yuan.service.UserService;
     5 import com.yuan.util.ImageUtil;
     6 import com.yuan.util.JSONResult;
     7 import com.yuan.util.JwtUtils;
     8 import com.yuan.util.VerifyCodeUtil;
     9 import io.jsonwebtoken.Claims;
    10 import org.springframework.beans.factory.annotation.Autowired;
    11 import org.springframework.data.redis.core.RedisTemplate;
    12 import org.springframework.stereotype.Controller;
    13 import org.springframework.util.StringUtils;
    14 import org.springframework.web.bind.annotation.RequestMapping;
    15 import org.springframework.web.bind.annotation.ResponseBody;
    16 
    17 import javax.servlet.http.HttpServletRequest;
    18 import javax.servlet.http.HttpServletResponse;
    19 import java.io.IOException;
    20 import java.util.HashMap;
    21 import java.util.Map;
    22 import java.util.concurrent.TimeUnit;
    23 
    24 @Controller
    25 @RequestMapping("/vue/user")
    26 public class UserController {
    27     private static final String VERIFICATION_CODE = "verificationCode_";
    28 
    29     @Autowired
    30     private UserService userService;
    31 
    32     @Autowired
    33     private RedisTemplate redisTemplate;
    34 
    35     @RequestMapping("/login")
    36     @ResponseBody
    37     public JSONResult login(User u, HttpServletRequest request, HttpServletResponse response){
    38         //获取用户输入的验证码
    39         String userVerificationCode = request.getParameter("verificationCode");
    40         //获取验证码jwt令牌
    41         String userJwt = request.getHeader(JwtUtils.JWT_VERIFICATION_KEY);
    42         //获取到保存在redis中的验证码
    43         Object redisVerificationCode =  redisTemplate.opsForValue().get(VERIFICATION_CODE + userJwt) ;
    44 
    45 //        这里存在两种情况:1、令牌超时   2、验证码超时
    46         if(StringUtils.isEmpty(redisVerificationCode)){
    47             return JSONResult.errorMsg("你的验证码已超时");
    48         }
    49 
    50         if(!redisVerificationCode.toString().equalsIgnoreCase(userVerificationCode)){
    51             return JSONResult.errorMsg("验证码错误");
    52         }
    53 
    54         User user = userService.login(u);
    55         //判断是否登录成功
    56         if(user != null){
    57             Map<String,Object> map=new HashMap<String, Object>();
    58             map.put("User", user);
    59             //这是颁发用户登录成功的jwt令牌
    60             String jwt= JwtUtils.createJwt(map, JwtUtils.JWT_WEB_TTL);
    61             response.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);
    62             return JSONResult.ok(user);
    63         }else {
    64             return JSONResult.errorMsg("密码或账户错误");
    65         }
    66         
    67     }
    68 
    69 
    70 
    71 
    72     /**生成图片验证码*/
    73     @RequestMapping("/verificationCode")
    74     @ResponseBody
    75     public String verificationCode(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    76         //生成验证码随机数
    77         String word = VerifyCodeUtil.produceNumAndChar(4);
    78 //        获取用户的jwt令牌
    79         String userVerificationJwt = req.getHeader(JwtUtils.JWT_VERIFICATION_KEY);
    80         //验证码令牌
    81         Claims claims = JwtUtils.validateJwtToken(userVerificationJwt);
    82         if(claims == null){
    83             //如果用户令牌过期那么对应存放在redis中的数据也要清空
    84             if(!StringUtils.isEmpty(userVerificationJwt)){
    85                 redisTemplate.expire(VERIFICATION_CODE + userVerificationJwt, 1, TimeUnit.DAYS);
    86             }
    87             userVerificationJwt =  JwtUtils.createJwt(new HashMap<String, Object>() ,JwtUtils.JWT_WEB_TTL);
    88             //将jwt令牌放入 response head中
    89             resp.setHeader(JwtUtils.JWT_VERIFICATION_KEY, userVerificationJwt);
    90         }
    91         //刷新缓存,更新验证码
    92         redisTemplate.opsForValue().set(VERIFICATION_CODE + userVerificationJwt , word,60, TimeUnit.SECONDS);
    93         //生成图片
    94         String code = "data:image/png;base64," + ImageUtil.createImageWithVerifyCode(word, 116,40);;
    95         return code;
    96     }
    97 }

    web.xml

     1 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
     2          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     3          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
     4          version="3.1">
     5   <display-name>Archetype Created Web Application</display-name>
     6 
     7   <welcome-file-list>
     8     <welcome-file>login.jsp</welcome-file>
     9   </welcome-file-list>
    10 
    11   <context-param>
    12     <param-name>contextConfigLocation</param-name>
    13     <param-value>classpath:applicationContext.xml</param-value>
    14   </context-param>
    15   <!-- 读取Spring上下文的监听器 -->
    16   <listener>
    17     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    18   </listener>
    19   <!-- Spring和web项目集成end -->
    20 
    21   <!-- 防止Spring内存溢出监听器 -->
    22   <listener>
    23     <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
    24   </listener>
    25 
    26     <!-- 中文乱码处理 -->
    27   <filter>
    28     <filter-name>encodingFilter</filter-name>
    29     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    30     <!--web.xml 3.0的新特性,是否支持异步-->
    31     <async-supported>true</async-supported>
    32     <init-param>
    33       <param-name>encoding</param-name>
    34       <param-value>UTF-8</param-value>
    35     </init-param>
    36   </filter>
    37   <filter-mapping>
    38     <filter-name>encodingFilter</filter-name>
    39     <url-pattern>/*</url-pattern>
    40   </filter-mapping>
    41   <!-- 解决cors跨域问题过滤器 -->
    42   <filter>
    43     <filter-name>corsFilter</filter-name>
    44     <filter-class>com.yuan.util.CorsFilter</filter-class>
    45   </filter>
    46   <filter-mapping>
    47     <filter-name>corsFilter</filter-name>
    48     <url-pattern>/*</url-pattern>
    49   </filter-mapping>
    50 
    51   <!-- Spring MVC servlet -->
    52   <servlet>
    53     <servlet-name>SpringMVC</servlet-name>
    54     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    55     <!--此参数可以不配置,默认值为:/WEB-INF/springmvc-servlet.xml-->
    56     <init-param>
    57       <param-name>contextConfigLocation</param-name>
    58       <param-value>/WEB-INF/springmvc-servlet.xml</param-value>
    59     </init-param>
    60     <load-on-startup>1</load-on-startup>
    61     <!--web.xml 3.0的新特性,是否支持异步-->
    62     <async-supported>true</async-supported>
    63   </servlet>
    64   <servlet-mapping>
    65     <servlet-name>SpringMVC</servlet-name>
    66     <url-pattern>/</url-pattern>
    67   </servlet-mapping>
    68 
    69 </web-app>

    最后看一下运行结果

     

    谢谢观看!!!

  • 相关阅读:
    B.Icebound and Sequence
    Educational Codeforces Round 65 (Rated for Div. 2) D. Bicolored RBS
    Educational Codeforces Round 65 (Rated for Div. 2) C. News Distribution
    Educational Codeforces Round 65 (Rated for Div. 2) B. Lost Numbers
    Educational Codeforces Round 65 (Rated for Div. 2) A. Telephone Number
    Codeforces Round #561 (Div. 2) C. A Tale of Two Lands
    Codeforces Round #561 (Div. 2) B. All the Vowels Please
    Codeforces Round #561 (Div. 2) A. Silent Classroom
    HDU-2119-Matrix(最大匹配)
    读书的感想!
  • 原文地址:https://www.cnblogs.com/ly-0919/p/11779761.html
Copyright © 2011-2022 走看看