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

    最后看一下运行结果

     

    谢谢观看!!!

  • 相关阅读:
    观察者模式的新纪录
    字符串和json之间的互相转化
    改变自己:工作几年就该给自己“清零”啦
    观察者模式的记录
    background-position的百分比
    豌豆荚前端面试中的一个算法题
    javascript的原型与继承(2)
    javascript的原型和继承(1)
    百度客户端首页的图片轮换效果
    javascript中闭包的概念
  • 原文地址:https://www.cnblogs.com/ly-0919/p/11779761.html
Copyright © 2020-2023  润新知