思路:
一、首先定義頁面,定義多功能表單(enctype=「multipart/form-data」)
二、在Controller裏面定義一個方法,用參數(MultipartFile)來接收前臺傳遞過來的文件對象
三、而後文件上傳就是把文件從一個地方(本地)複製到另一個地方(服務器)html
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency>
<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"; }
<%-- 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>
思路:
登陸界面向後臺請求驗證碼,後臺就先調用隨機函數生成驗證碼,而且根據驗證碼生成一張圖片,以 base64 字符串的形式傳到前臺,這時咱們還要生成verificationJwt令牌作爲請求驗證碼客戶端的區分。咱們先將驗證碼信息存入redis。key是 verificationJwt令牌的值,value就是驗證碼了。而且將令牌放入到響應頭。傳給客戶端。當客戶端提交的時候將保持的verificationJwt令牌放入請求頭帶過來。後端根據前端傳過來的 jwt令牌去redis中獲取數據,將驗證碼拿到後和現有的驗證碼進行比較。看看是否相等
細節:
訪問一次登陸頁面,生成一個verificationJwt令牌,這個令牌的有效時間是5min,驗證碼在redis中的有效時間是1min,不管是verificationJwt令牌超時失效,仍是驗證碼生成後超時失效,都會形成登陸失敗;
簡單來講就是跳轉到登錄界面,須要在5min中以內完成登陸,新的驗證碼出來後,須要在1min中以內完成登陸前端
<!--引入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>
State.jsvue
export default { resturantName: '每天餐館', jwt:'', options: [],//存放tab頁的容器 activeIndex: '',//激活的tab頁路由路徑 showName:'show',//tab頁的標題 role:""//用來區分是不是由於左側菜單被點擊形成的路由路徑發生改變,是:pass;不是:nopass verificationJwt:null, //這是用來保存用戶等登陸驗證碼jwt身份識別的 }
Mutations.jsjava
setVerificationJwt: (state, payload) => { state.verificationJwt = payload.verificationJwt; },
Getters.jsios
getVerificationJwt:(state) =>{ return state.verificationJwt; },
action.jsweb
/** * 對後臺請求的地址的封裝,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.jsajax
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.vueredis
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="width: 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 width: 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 width: 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>
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 }
ImageUtilspring
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("\n", "").replaceAll("\r", "");//刪除 \r\n 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 }
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 // 這裏存在兩種狀況:一、令牌超時 二、驗證碼超時 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>