Commit 679117d5 authored by linlinjava's avatar linlinjava
Browse files

feat: 支持验证码

parent 519d7b15
......@@ -40,6 +40,10 @@
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
......
package org.linlinjava.litemall.admin.config;
import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class KaptchaConfig {
@Bean
public Producer kaptchaProducer() {
Properties properties = new Properties();
properties.setProperty("kaptcha.image.width", "100");
properties.setProperty("kaptcha.image.height", "40");
properties.setProperty("kaptcha.textproducer.font.size", "32");
properties.setProperty("kaptcha.textproducer.font.color", "0,0,0");
properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYAZ");
properties.setProperty("kaptcha.textproducer.char.length", "4");
properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
DefaultKaptcha kaptcha = new DefaultKaptcha();
Config config = new Config(properties);
kaptcha.setConfig(config);
return kaptcha;
}
}
\ No newline at end of file
......@@ -29,6 +29,7 @@ public class ShiroConfig {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("/admin/auth/kaptcha", "anon");
filterChainDefinitionMap.put("/admin/auth/login", "anon");
filterChainDefinitionMap.put("/admin/auth/401", "anon");
filterChainDefinitionMap.put("/admin/auth/index", "anon");
......
......@@ -18,7 +18,7 @@ public class AdminWebSessionManager extends DefaultWebSessionManager {
public AdminWebSessionManager() {
super();
setGlobalSessionTimeout(MILLIS_PER_HOUR * 6);
setSessionIdCookieEnabled(false);
// setSessionIdCookieEnabled(false);
setSessionIdUrlRewritingEnabled(false);
}
......
......@@ -7,6 +7,8 @@ public class AdminResponseCode {
public static final Integer ADMIN_ALTER_NOT_ALLOWED = 603;
public static final Integer ADMIN_DELETE_NOT_ALLOWED = 604;
public static final Integer ADMIN_INVALID_ACCOUNT = 605;
public static final Integer ADMIN_INVALID_KAPTCHA = 606;
public static final Integer ADMIN_INVALID_KAPTCHA_REQUIRED = 607;
public static final Integer GOODS_UPDATE_NOT_ALLOWED = 610;
public static final Integer GOODS_NAME_EXIST = 611;
public static final Integer ORDER_CONFIRM_NOT_ALLOWED = 620;
......
package org.linlinjava.litemall.admin.web;
import com.google.code.kaptcha.Producer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
......@@ -24,12 +25,18 @@ import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import static org.linlinjava.litemall.admin.util.AdminResponseCode.ADMIN_INVALID_ACCOUNT;
import static org.linlinjava.litemall.admin.util.AdminResponseCode.*;
@RestController
@RequestMapping("/admin/auth")
......@@ -46,6 +53,37 @@ public class AdminAuthController {
@Autowired
private LogHelper logHelper;
@Autowired
private Producer kaptchaProducer;
@GetMapping("/kaptcha")
public Object kaptcha(HttpServletRequest request) {
String kaptcha = doKaptcha(request);
if (kaptcha != null) {
return ResponseUtil.ok(kaptcha);
}
return ResponseUtil.fail();
}
private String doKaptcha(HttpServletRequest request) {
// 生成验证码
String text = kaptchaProducer.createText();
BufferedImage image = kaptchaProducer.createImage(text);
HttpSession session = request.getSession();
session.setAttribute("kaptcha", text);
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpeg", outputStream);
BASE64Encoder encoder = new BASE64Encoder();
String base64 = encoder.encode(outputStream.toByteArray());
String captchaBase64 = "data:image/jpeg;base64," + base64.replaceAll("\r\n", "");
return captchaBase64;
} catch (IOException e) {
return null;
}
}
/*
* { username : value, password : value }
*/
......@@ -53,10 +91,20 @@ public class AdminAuthController {
public Object login(@RequestBody String body, HttpServletRequest request) {
String username = JacksonUtil.parseString(body, "username");
String password = JacksonUtil.parseString(body, "password");
String code = JacksonUtil.parseString(body, "code");
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
return ResponseUtil.badArgument();
}
if (StringUtils.isEmpty(code)) {
return ResponseUtil.fail(ADMIN_INVALID_KAPTCHA_REQUIRED, "验证码不能空");
}
HttpSession session = request.getSession();
String kaptcha = (String)session.getAttribute("kaptcha");
if (Objects.requireNonNull(code).compareToIgnoreCase(kaptcha) != 0) {
return ResponseUtil.fail(ADMIN_INVALID_KAPTCHA, "验证码不正确", doKaptcha(request));
}
Subject currentUser = SecurityUtils.getSubject();
try {
......
import request from '@/utils/request'
export function loginByUsername(username, password) {
export function loginByUsername(username, password, code) {
const data = {
username,
password
password,
code
}
return request({
url: '/auth/login',
......@@ -27,3 +28,9 @@ export function getUserInfo(token) {
})
}
export function getKaptcha() {
return request({
url: '/auth/kaptcha',
method: 'get'
})
}
......@@ -35,7 +35,7 @@ const user = {
LoginByUsername({ commit }, userInfo) {
const username = userInfo.username.trim()
return new Promise((resolve, reject) => {
loginByUsername(username, userInfo.password).then(response => {
loginByUsername(username, userInfo.password, userInfo.code).then(response => {
const token = response.data.data.token
commit('SET_TOKEN', token)
setToken(token)
......
......@@ -3,6 +3,8 @@ import { Message, MessageBox } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
axios.defaults.withCredentials = true
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
......
......@@ -18,6 +18,16 @@
<el-input v-model="loginForm.password" :type="passwordType" name="password" auto-complete="on" tabindex="2" show-password placeholder="管理员密码" @keyup.enter.native="handleLogin" />
</el-form-item>
<el-form-item prop="code">
<span class="svg-container">
<svg-icon icon-class="lock" />
</span>
<el-input v-model="loginForm.code" auto-complete="off" name="code" tabindex="2" placeholder="验证码" style="width: 60%" @keyup.enter.native="handleLogin" />
<div class="login-code">
<img :src="codeImg" @click="getCode">
</div>
</el-form-item>
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button>
<div style="position:relative">
......@@ -43,6 +53,8 @@
</template>
<script>
import { getKaptcha } from '@/api/login'
export default {
name: 'Login',
data() {
......@@ -56,8 +68,10 @@ export default {
return {
loginForm: {
username: 'admin123',
password: 'admin123'
password: 'admin123',
code: ''
},
codeImg: '',
loginRules: {
username: [{ required: true, message: '管理员账户不允许为空', trigger: 'blur' }],
password: [
......@@ -79,12 +93,18 @@ export default {
},
created() {
this.getCode()
// window.addEventListener('hashchange', this.afterQRScan)
},
destroyed() {
// window.removeEventListener('hashchange', this.afterQRScan)
},
methods: {
getCode() {
getKaptcha().then(response => {
this.codeImg = response.data.data
})
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid && !this.loading) {
......@@ -93,6 +113,9 @@ export default {
this.loading = false
this.$router.push({ path: this.redirect || '/' })
}).catch(response => {
if (response.data.errno === 606 || response.data.errno === 605) {
this.codeImg = response.data.data
}
this.$notify.error({
title: '失败',
message: response.data.errmsg
......@@ -174,7 +197,14 @@ $light_gray:#eee;
margin: 0 auto;
overflow: hidden;
}
.login-code {
padding-top: 5px;
float: right;
img{
cursor: pointer;
vertical-align:middle
}
}
.tips {
font-size: 14px;
color: #fff;
......
......@@ -17,6 +17,7 @@ public class CorsConfig {
corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
corsConfiguration.setMaxAge(maxAge);
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}
......
......@@ -109,6 +109,14 @@ public class ResponseUtil {
return obj;
}
public static Object fail(int errno, String errmsg, String data) {
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("errno", errno);
obj.put("errmsg", errmsg);
obj.put("data", data);
return obj;
}
public static Object badArgument() {
return fail(401, "参数不对");
}
......
......@@ -154,7 +154,13 @@
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<!--引入ui包-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment