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

feat: 支持验证码

parent 519d7b15
...@@ -40,6 +40,10 @@ ...@@ -40,6 +40,10 @@
<groupId>org.apache.shiro</groupId> <groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId> <artifactId>shiro-spring-boot-web-starter</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.github.xiaoymin</groupId> <groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId> <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 { ...@@ -29,6 +29,7 @@ public class ShiroConfig {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("/admin/auth/kaptcha", "anon");
filterChainDefinitionMap.put("/admin/auth/login", "anon"); filterChainDefinitionMap.put("/admin/auth/login", "anon");
filterChainDefinitionMap.put("/admin/auth/401", "anon"); filterChainDefinitionMap.put("/admin/auth/401", "anon");
filterChainDefinitionMap.put("/admin/auth/index", "anon"); filterChainDefinitionMap.put("/admin/auth/index", "anon");
......
...@@ -18,7 +18,7 @@ public class AdminWebSessionManager extends DefaultWebSessionManager { ...@@ -18,7 +18,7 @@ public class AdminWebSessionManager extends DefaultWebSessionManager {
public AdminWebSessionManager() { public AdminWebSessionManager() {
super(); super();
setGlobalSessionTimeout(MILLIS_PER_HOUR * 6); setGlobalSessionTimeout(MILLIS_PER_HOUR * 6);
setSessionIdCookieEnabled(false); // setSessionIdCookieEnabled(false);
setSessionIdUrlRewritingEnabled(false); setSessionIdUrlRewritingEnabled(false);
} }
......
...@@ -7,6 +7,8 @@ public class AdminResponseCode { ...@@ -7,6 +7,8 @@ public class AdminResponseCode {
public static final Integer ADMIN_ALTER_NOT_ALLOWED = 603; public static final Integer ADMIN_ALTER_NOT_ALLOWED = 603;
public static final Integer ADMIN_DELETE_NOT_ALLOWED = 604; public static final Integer ADMIN_DELETE_NOT_ALLOWED = 604;
public static final Integer ADMIN_INVALID_ACCOUNT = 605; 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_UPDATE_NOT_ALLOWED = 610;
public static final Integer GOODS_NAME_EXIST = 611; public static final Integer GOODS_NAME_EXIST = 611;
public static final Integer ORDER_CONFIRM_NOT_ALLOWED = 620; public static final Integer ORDER_CONFIRM_NOT_ALLOWED = 620;
......
package org.linlinjava.litemall.admin.web; package org.linlinjava.litemall.admin.web;
import com.google.code.kaptcha.Producer;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
...@@ -24,12 +25,18 @@ import org.springframework.context.ApplicationContext; ...@@ -24,12 +25,18 @@ import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest; 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.time.LocalDateTime;
import java.util.*; import java.util.*;
import static org.linlinjava.litemall.admin.util.AdminResponseCode.ADMIN_INVALID_ACCOUNT; import static org.linlinjava.litemall.admin.util.AdminResponseCode.*;
@RestController @RestController
@RequestMapping("/admin/auth") @RequestMapping("/admin/auth")
...@@ -46,6 +53,37 @@ public class AdminAuthController { ...@@ -46,6 +53,37 @@ public class AdminAuthController {
@Autowired @Autowired
private LogHelper logHelper; 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 } * { username : value, password : value }
*/ */
...@@ -53,10 +91,20 @@ public class AdminAuthController { ...@@ -53,10 +91,20 @@ public class AdminAuthController {
public Object login(@RequestBody String body, HttpServletRequest request) { public Object login(@RequestBody String body, HttpServletRequest request) {
String username = JacksonUtil.parseString(body, "username"); String username = JacksonUtil.parseString(body, "username");
String password = JacksonUtil.parseString(body, "password"); String password = JacksonUtil.parseString(body, "password");
String code = JacksonUtil.parseString(body, "code");
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
return ResponseUtil.badArgument(); 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(); Subject currentUser = SecurityUtils.getSubject();
try { try {
......
import request from '@/utils/request' import request from '@/utils/request'
export function loginByUsername(username, password) { export function loginByUsername(username, password, code) {
const data = { const data = {
username, username,
password password,
code
} }
return request({ return request({
url: '/auth/login', url: '/auth/login',
...@@ -27,3 +28,9 @@ export function getUserInfo(token) { ...@@ -27,3 +28,9 @@ export function getUserInfo(token) {
}) })
} }
export function getKaptcha() {
return request({
url: '/auth/kaptcha',
method: 'get'
})
}
...@@ -35,7 +35,7 @@ const user = { ...@@ -35,7 +35,7 @@ const user = {
LoginByUsername({ commit }, userInfo) { LoginByUsername({ commit }, userInfo) {
const username = userInfo.username.trim() const username = userInfo.username.trim()
return new Promise((resolve, reject) => { 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 const token = response.data.data.token
commit('SET_TOKEN', token) commit('SET_TOKEN', token)
setToken(token) setToken(token)
......
...@@ -3,6 +3,8 @@ import { Message, MessageBox } from 'element-ui' ...@@ -3,6 +3,8 @@ import { Message, MessageBox } from 'element-ui'
import store from '@/store' import store from '@/store'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
axios.defaults.withCredentials = true
// create an axios instance // create an axios instance
const service = axios.create({ const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
......
...@@ -18,6 +18,16 @@ ...@@ -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-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>
<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> <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button>
<div style="position:relative"> <div style="position:relative">
...@@ -43,6 +53,8 @@ ...@@ -43,6 +53,8 @@
</template> </template>
<script> <script>
import { getKaptcha } from '@/api/login'
export default { export default {
name: 'Login', name: 'Login',
data() { data() {
...@@ -56,8 +68,10 @@ export default { ...@@ -56,8 +68,10 @@ export default {
return { return {
loginForm: { loginForm: {
username: 'admin123', username: 'admin123',
password: 'admin123' password: 'admin123',
code: ''
}, },
codeImg: '',
loginRules: { loginRules: {
username: [{ required: true, message: '管理员账户不允许为空', trigger: 'blur' }], username: [{ required: true, message: '管理员账户不允许为空', trigger: 'blur' }],
password: [ password: [
...@@ -79,12 +93,18 @@ export default { ...@@ -79,12 +93,18 @@ export default {
}, },
created() { created() {
this.getCode()
// window.addEventListener('hashchange', this.afterQRScan) // window.addEventListener('hashchange', this.afterQRScan)
}, },
destroyed() { destroyed() {
// window.removeEventListener('hashchange', this.afterQRScan) // window.removeEventListener('hashchange', this.afterQRScan)
}, },
methods: { methods: {
getCode() {
getKaptcha().then(response => {
this.codeImg = response.data.data
})
},
handleLogin() { handleLogin() {
this.$refs.loginForm.validate(valid => { this.$refs.loginForm.validate(valid => {
if (valid && !this.loading) { if (valid && !this.loading) {
...@@ -93,6 +113,9 @@ export default { ...@@ -93,6 +113,9 @@ export default {
this.loading = false this.loading = false
this.$router.push({ path: this.redirect || '/' }) this.$router.push({ path: this.redirect || '/' })
}).catch(response => { }).catch(response => {
if (response.data.errno === 606 || response.data.errno === 605) {
this.codeImg = response.data.data
}
this.$notify.error({ this.$notify.error({
title: '失败', title: '失败',
message: response.data.errmsg message: response.data.errmsg
...@@ -174,7 +197,14 @@ $light_gray:#eee; ...@@ -174,7 +197,14 @@ $light_gray:#eee;
margin: 0 auto; margin: 0 auto;
overflow: hidden; overflow: hidden;
} }
.login-code {
padding-top: 5px;
float: right;
img{
cursor: pointer;
vertical-align:middle
}
}
.tips { .tips {
font-size: 14px; font-size: 14px;
color: #fff; color: #fff;
......
...@@ -17,6 +17,7 @@ public class CorsConfig { ...@@ -17,6 +17,7 @@ public class CorsConfig {
corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头 corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法 corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
corsConfiguration.setMaxAge(maxAge); corsConfiguration.setMaxAge(maxAge);
corsConfiguration.setAllowCredentials(true);
return corsConfiguration; return corsConfiguration;
} }
......
...@@ -109,6 +109,14 @@ public class ResponseUtil { ...@@ -109,6 +109,14 @@ public class ResponseUtil {
return obj; 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() { public static Object badArgument() {
return fail(401, "参数不对"); return fail(401, "参数不对");
} }
......
...@@ -154,7 +154,13 @@ ...@@ -154,7 +154,13 @@
<artifactId>springfox-swagger-ui</artifactId> <artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version> <version>2.9.2</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<!--引入ui包--> <!--引入ui包-->
<dependency> <dependency>
<groupId>com.github.xiaoymin</groupId> <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