后端使用SpringBoot和Jwt工具与Redis数据库+前端Vue Element Admin实现用户携带token的登录功能案例
前端输入对应的用户信息 ,在用户输入邮箱后 点击发送按钮给邮箱发送验证码,拿取到验证填完之后,点击登录按钮给后端发送请求 ,后端接收到请求之后 ,第一步校验验证码,第二步校验用户名和密码是否正确,通过验证之后,表示登录成功,再给前端响应数据(之前返回的是登录用户,现在使用的是token,它是给返回的对象进行加密), 前端拿到后端响应的数据token, ,再次给后端发送请求,查询该用户的角色,前端拿
登录功能描述:
前端输入对应的用户信息 ,在用户输入邮箱后 点击发送按钮给邮箱发送验证码,拿取到验证填完之后,点击登录按钮给后端发送请求 ,后端接收到请求之后 ,第一步校验验证码,第二步校验用户名和密码是否正确,通过验证之后,表示登录成功,再给前端响应数据(之前返回的是登录用户,现在使用的是token,它是给返回的对象进行加密), 前端拿到后端响应的数据token, ,再次给后端发送请求,查询该用户的角色, 前端拿取到用户的角色之后,根据角色的不同跳转到不同的页面;
前端页面:
1.前端基本 登录页面
<template>
<div class="login-container">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left">
<div class="title-container">
<h3 class="title">登录</h3>
</div>
<el-form-item prop="username">
<span class="svg-container">
<svg-icon icon-class="user" />
</span>
<el-input
ref="username"
v-model="loginForm.username"
placeholder="Username"
name="username"
type="text"
tabindex="1"
autocomplete="on"
/>
</el-form-item>
<el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password" />
</span>
<el-input
:key="passwordType"
ref="password"
v-model="loginForm.password"
:type="passwordType"
placeholder="Password"
name="password"
tabindex="2"
autocomplete="on"
@keyup.native="checkCapslock"
@blur="capsTooltip = false"
@keyup.enter.native="handleLogin"
/>
<span class="show-pwd" @click="showPwd">
<svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
</span>
</el-form-item>
</el-tooltip>
<el-form-item prop="email">
<span class="svg-container">
<svg-icon icon-class="user" />
</span>
<el-input
ref="email"
v-model="loginForm.email"
placeholder="请输入邮箱"
name="email"
type="text"
tabindex="3"
style="width: 70%;"
autocomplete="on"
/>
<el-button type="primary" style="width:20%;" @click.native.prevent="sendEmail">发送</el-button>
</el-form-item>
<el-form-item prop="verifycode">
<span class="svg-container">
<svg-icon icon-class="user" />
</span>
<el-input
ref="verifycode"
v-model="loginForm.verifycode"
placeholder="请输入验证码"
name="verifycode"
type="text"
tabindex="4"
autocomplete="on"
/>
</el-form-item>
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button>
<el-button type="primary" style="width:100%;margin-bottom:30px;margin-left: 0px;" @click="$router.push('/register')">注册</el-button>
<div style="position:relative">
<div class="tips">
<span>Username : admin</span>
<span>Password : any</span>
</div>
<div class="tips">
<span style="margin-right:18px;">Username : editor</span>
<span>Password : any</span>
</div>
<el-button class="thirdparty-button" type="primary" @click="showDialog=true">
Or connect with
</el-button>
</div>
</el-form>
<el-dialog title="Or connect with" :visible.sync="showDialog">
Can not be simulated on local, so please combine you own business simulation! ! !
<br>
<br>
<br>
<social-sign />
</el-dialog>
</div>
</template>
<script>
import { validUsername } from '@/utils/validate'
import {sendEmail} from '@/api/user'
import SocialSign from './components/SocialSignin'
export default {
name: 'Login',
components: { SocialSign },
data() {
const validateUsername = (rule, value, callback) => {
if (!validUsername(value)) {
callback(new Error('Please enter the correct user name'))
} else {
callback()
}
}
const validatePassword = (rule, value, callback) => {
if (value.length < 6) {
callback(new Error('The password can not be less than 6 digits'))
} else {
callback()
}
}
return {
loginForm: {
username: 'admin',
password: '111111',
email:'wangshan20010907@126.com'
},
loginRules: {
// username: [{ required: true, trigger: 'blur', validator: validateUsername }],
// password: [{ required: true, trigger: 'blur', validator: validatePassword }]
},
passwordType: 'password',
capsTooltip: false,
loading: false,
showDialog: false,
redirect: undefined,
otherQuery: {}
}
},
watch: {
$route: {
handler: function(route) {
const query = route.query
if (query) {
this.redirect = query.redirect
this.otherQuery = this.getOtherQuery(query)
}
},
immediate: true
}
},
created() {
// window.addEventListener('storage', this.afterQRScan)
},
mounted() {
if (this.loginForm.username === '') {
this.$refs.username.focus()
} else if (this.loginForm.password === '') {
this.$refs.password.focus()
}
},
destroyed() {
// window.removeEventListener('storage', this.afterQRScan)
},
methods: {
//发送邮箱
async sendEmail(){
let {message}=await sendEmail(this.loginForm);
this.$message.success(message);
},
checkCapslock(e) {
const { key } = e
this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z')
},
showPwd() {
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.password.focus()
})
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
this.$store.dispatch('user/login', this.loginForm)
.then((data) => {
this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
this.loading = false
})
.catch((error) => {
console.info('@/views/login:165',error)
this.loading = false
})
} else {
console.log('error submit!!')
return false
}
})
},
getOtherQuery(query) {
return Object.keys(query).reduce((acc, cur) => {
if (cur !== 'redirect') {
acc[cur] = query[cur]
}
return acc
}, {})
}
// afterQRScan() {
// if (e.key === 'x-admin-oauth-code') {
// const code = getQueryObject(e.newValue)
// const codeMap = {
// wechat: 'code',
// tencent: 'code'
// }
// const type = codeMap[this.auth_type]
// const codeName = code[type]
// if (codeName) {
// this.$store.dispatch('LoginByThirdparty', codeName).then(() => {
// this.$router.push({ path: this.redirect || '/' })
// })
// } else {
// alert('第三方登录失败')
// }
// }
// }
}
}
</script>
<style lang="scss">
/* 修复input 背景不协调 和光标变色 */
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
$bg:#283443;
$light_gray:#fff;
$cursor: #fff;
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
.login-container .el-input input {
color: $cursor;
}
}
/* reset element-ui css */
.login-container {
.el-input {
display: inline-block;
height: 47px;
width: 85%;
input {
background: transparent;
border: 0px;
-webkit-appearance: none;
border-radius: 0px;
padding: 12px 5px 12px 15px;
color: $light_gray;
height: 47px;
caret-color: $cursor;
&:-webkit-autofill {
box-shadow: 0 0 0px 1000px $bg inset !important;
-webkit-text-fill-color: $cursor !important;
}
}
}
.el-form-item {
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.1);
border-radius: 5px;
color: #454545;
}
}
</style>
<style lang="scss" scoped>
$bg:#2d3a4b;
$dark_gray:#889aa4;
$light_gray:#eee;
.login-container {
min-height: 100%;
width: 100%;
background-color: $bg;
overflow: hidden;
.login-form {
position: relative;
width: 520px;
max-width: 100%;
padding: 160px 35px 0;
margin: 0 auto;
overflow: hidden;
}
.tips {
font-size: 14px;
color: #fff;
margin-bottom: 10px;
span {
&:first-of-type {
margin-right: 16px;
}
}
}
.svg-container {
padding: 6px 5px 6px 15px;
color: $dark_gray;
vertical-align: middle;
width: 30px;
display: inline-block;
}
.title-container {
position: relative;
.title {
font-size: 26px;
color: $light_gray;
margin: 0px auto 40px auto;
text-align: center;
font-weight: bold;
}
}
.show-pwd {
position: absolute;
right: 10px;
top: 7px;
font-size: 16px;
color: $dark_gray;
cursor: pointer;
user-select: none;
}
.thirdparty-button {
position: absolute;
right: 0;
bottom: 6px;
}
@media only screen and (max-width: 470px) {
.thirdparty-button {
display: none;
}
}
}
</style>
2.登录页面的路由配置
3.修改框架模拟的路径 换成我们自己的后端逻辑
vue 可以自动生成这些代码 .注意的地方都标注出来了
后端:
1.子类pom文件依赖
<dependencies>
<!--web起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<!-- feign 远程调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- mybatis plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--自定义项目-->
<dependency>
<groupId>com.czxy</groupId>
<artifactId>zx_common01</artifactId>
</dependency>
<dependency>
<groupId>com.czxy</groupId>
<artifactId>zx_domain01</artifactId>
</dependency>
<!-- redis 启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- JavaMail 启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- MQ 启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<!--JavaBean工具类,用于JavaBean数据封装-->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<!--jwt工具-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<!--joda 时间工具类 -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
</dependencies>
application.yml
# 服务端口号
server:
port: 9010
# 服务名
spring:
application:
name: user-service
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/zx_edu_user?useUnicode=true&characterEncoding=utf8
username: root
password: root
druid: #druid 连接池配置
initial-size: 1 #初始化连接池大小
min-idle: 1 #最小连接数
max-active: 20 #最大连接数
test-on-borrow: true #获取连接时候验证,会影响性能
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos服务地址
redis:
database: 0 #数据库索引,取值0-15,表示16个库可选择
host: 127.0.0.1 #服务器地址
port: 6379 #服务器连接端口号
mail:
host: smtp.163.com #发送邮件服务器
username: wangshan20010907@163.com #账号
password: IBCEGYGFTZMQFFQY #授权密码
default-encoding: UTF-8 #默认编码时
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
passowrd: guest
virtualHost: /
freemarker:
cache: false #页面不加载缓存,修改即时生效
#开启log4j打印SQL语句
logging:
level:
com:
czxy:
zx:
user:
mapper: debug
# mp日志打印
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
sc:
jwt:
secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
pubKeyPath: D:/rsa/rsa.pub # 公钥地址
priKeyPath: D:/rsa/rsa.pri # 私钥地址
expire: 360 # 过期时间,单位分钟
3.配置类utils:
验证码VerifyCodeUtils :
package com.czxy.zx.utils;
import com.czxy.zx.vo.VerifyCode;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
public class VerifyCodeUtils {
//获取验证码
public static VerifyCode getVerifyCode(){
VerifyCode verifyCode = new VerifyCode();
//字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
int IMG_WIDTH = 72;
int IMG_HEIGTH = 27;
Random random = new Random();
//创建图片
BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGTH, BufferedImage.TYPE_INT_RGB);
//画板
Graphics g = image.getGraphics();
//填充背景
g.setColor(Color.WHITE);
g.fillRect(1,1,IMG_WIDTH-2,IMG_HEIGTH-2);
g.setFont(new Font("楷体",Font.BOLD,25));
StringBuilder sb = new StringBuilder();
//写字
for(int i = 1 ; i <= 4 ; i ++){
//随机颜色
g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
int len = random.nextInt(VERIFY_CODES.length());
String str = VERIFY_CODES.substring(len,len+1);
sb.append(str);
g.drawString(str, IMG_WIDTH / 6 * i , 22 );
}
// 生成随机干扰线
for (int i = 0; i < 30; i++) {
//随机颜色
g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
int x = random.nextInt(IMG_WIDTH - 1);
int y = random.nextInt(IMG_HEIGTH - 1);
int x1 = random.nextInt(12) + 1;
int y1 = random.nextInt(6) + 1;
g.drawLine(x, y, x - x1, y - y1);
}
verifyCode.setCode(sb.toString());
verifyCode.setImage(image);
verifyCode.setType("jpeg");
return verifyCode;
}
}
JwtUtils类:
package com.czxy.zx.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.beanutils.BeanUtils;
import org.joda.time.DateTime;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* Created by wangshan.
*/
public class JwtUtils {
/**
* 私钥加密token
* @param data 需要加密的数据(载荷内容)
* @param expireMinutes 过期时间,单位:分钟
* @param privateKey 私钥
* @return
*/
public static String generateToken(Object data, int expireMinutes, PrivateKey privateKey) {
try {
//1 获得jwt构建对象
JwtBuilder jwtBuilder = Jwts.builder();
//2 设置数据
if( data == null ) {
throw new RuntimeException("数据不能为空");
}
BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
// 获得属性名
String name = propertyDescriptor.getName();
// 获得属性值
Object value = propertyDescriptor.getReadMethod().invoke(data);
if(value != null) {
jwtBuilder.claim(name,value);
}
}
//3 设置过期时间
jwtBuilder.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate());
//4 设置加密
jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKey);
//5 构建
return jwtBuilder.compact();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 通过公钥解析token
* @param token 需要解析的数据
* @param publicKey 公钥
* @param beanClass 封装的JavaBean
* @return
* @throws Exception
*/
public static <T> T getObjectFromToken(String token, PublicKey publicKey,Class<T> beanClass) throws Exception {
//1 获得解析后内容
Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
//2 将内容封装到对象JavaBean
T bean = beanClass.newInstance();
BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
// 获得属性名
String name = propertyDescriptor.getName();
// 通过属性名,获得对应解析的数据
Object value = body.get(name);
if(value != null) {
// 将获得的数据封装到对应的JavaBean中
BeanUtils.setProperty(bean,name,value);
}
}
return bean;
}
}
RsaUtils类
package com.czxy.zx.utils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* Created by wangshan.
*/
public class RsaUtils {
/**
* 从文件中读取公钥
*
* @param filename 公钥保存路径,相对于classpath
* @return 公钥对象
* @throws Exception
*/
public static PublicKey getPublicKey(String filename) throws Exception {
byte[] bytes = readFile(filename);
return getPublicKey(bytes);
}
/**
* 从文件中读取密钥
*
* @param filename 私钥保存路径,相对于classpath
* @return 私钥对象
* @throws Exception
*/
public static PrivateKey getPrivateKey(String filename) throws Exception {
byte[] bytes = readFile(filename);
return getPrivateKey(bytes);
}
/**
* 获取公钥
*
* @param bytes 公钥的字节形式
* @return
* @throws Exception
*/
public static PublicKey getPublicKey(byte[] bytes) throws Exception {
X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(spec);
}
/**
* 获取密钥
*
* @param bytes 私钥的字节形式
* @return
* @throws Exception
*/
public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePrivate(spec);
}
/**
* 根据密文,生存rsa公钥和私钥,并写入指定文件
*
* @param publicKeyFilename 公钥文件路径
* @param privateKeyFilename 私钥文件路径
* @param secret 生成密钥的密文
* @throws Exception
*/
public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom secureRandom = new SecureRandom(secret.getBytes());
keyPairGenerator.initialize(1024, secureRandom);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// 获取公钥并写出
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
writeFile(publicKeyFilename, publicKeyBytes);
// 获取私钥并写出
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
writeFile(privateKeyFilename, privateKeyBytes);
}
private static byte[] readFile(String fileName) throws Exception {
return Files.readAllBytes(new File(fileName).toPath());
}
private static void writeFile(String destPath, byte[] bytes) throws IOException {
File dest = new File(destPath);
//创建父文件夹
if(!dest.getParentFile().exists()){
dest.getParentFile().mkdirs();
}
//创建需要的文件
if (!dest.exists()) {
dest.createNewFile();
}
Files.write(dest.toPath(), bytes);
}
}
3.配置类config:
JwtProperties :
package com.czxy.zx.user.config;
import com.czxy.zx.utils.RsaUtils;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;
@Configuration
@ConfigurationProperties(prefix = "sc.jwt")
@Data
public class JwtProperties {
private String secret;
private String pubKeyPath;
private String priKeyPath;
private Integer expire;
private PublicKey publicKey;
private PrivateKey privateKey;
@PostConstruct //初始化方法注解
public void init() {
try {
File pubFile = new File(pubKeyPath);
File priFile = new File(priKeyPath);
if(!pubFile.exists() || ! priFile.exists()) {
RsaUtils.generateKey(pubKeyPath,priKeyPath,secret);
}
// 获得公钥和私钥对象
this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
this.privateKey = RsaUtils.getPrivateKey(priKeyPath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
MyBatisPlusConfig :
package com.czxy.zx.user.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
}
}
免前端页面测试:
Swagger2ConfigurationV3
package com.czxy.zx.user.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* Swagger2 配置类,
* 访问路径:swagger-ui.html
* 自动注册:
* 位置:resources/META-INF/spring.factories
* 内容:
* org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
* com.czxy.changgou4.config.Swagger2Configuration
*/
@Configuration
@EnableSwagger2
public class Swagger2ConfigurationV3 {
@Bean
public Docket createRestApi() {
// 1 确定文档Swagger版本
Docket docket = new Docket(DocumentationType.SWAGGER_2);
// 2 设置 api基本信息
docket.apiInfo(apiInfo());
// 3 设置自定义加载路径
docket = docket.select()
.apis(RequestHandlerSelectors.basePackage("com.czxy"))
.paths(PathSelectors.any())
.build();
//4 设置权限
docket.securitySchemes(securitySchemes());
docket.securityContexts(securityContexts());
return docket;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("API")
.description("基于swagger接口文档")
.contact(new Contact("java34","http://www.javaliang.com","liangtong@itcast.cn"))
.version("1.0")
.build();
}
private List<ApiKey> securitySchemes() {
List<ApiKey> list = new ArrayList<>();
// name 为参数名 keyname是页面传值显示的 keyname, name在swagger鉴权中使用
list.add(new ApiKey("Authorization", "Authorization", "header"));
return list;
}
private List<SecurityContext> securityContexts() {
List<SecurityContext> list = new ArrayList<>();
list.add(SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("^(?!auth).*$"))
.build());
return list;
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> list = new ArrayList();
list.add(new SecurityReference("Authorization", authorizationScopes));
return list;
}
}
队列的配置类:
RabbitEmailConfig
package com.czxy.zx.user.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class RabbitEmailConfig {
//队列名称
public static final String QUEUE_LOGIN_EMAIL="login_email";
public static final String QUEUE_REGISTER_EMAIL="register_email";
//生成队列对象
@Bean(QUEUE_LOGIN_EMAIL)
public Queue queue(){
return new Queue(QUEUE_LOGIN_EMAIL);
}
//生成队列对象
@Bean(QUEUE_REGISTER_EMAIL)
public Queue queue2(){
return new Queue(QUEUE_REGISTER_EMAIL);
}
}
登录service:
package com.czxy.zx.user.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.zx.domain.EduUser;
import com.czxy.zx.exception.EduException;
import com.czxy.zx.user.mapper.EduUserMapper;
import com.czxy.zx.user.service.EduUserService;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
@Service
@Transactional
public class EduUserServiceImpl extends ServiceImpl<EduUserMapper, EduUser> implements EduUserService {
@Override
public boolean register(EduUser user) {
QueryWrapper<EduUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",user.getUsername());
EduUser selectOne = baseMapper.selectOne(queryWrapper);
if(selectOne!=null){
//用户已注册
throw new EduException("该用户名已经存在啦");
}
user.setStatus("0");
user.setCreated(new Date());
int insert = baseMapper.insert(user);
return insert==1;
}
@Override
public EduUser login(EduUser user) {
QueryWrapper<EduUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(StringUtils.isNotBlank(user.getUsername()),"username",user.getUsername());
queryWrapper.eq(StringUtils.isNotBlank(user.getPassword()),"password",user.getPassword());
List<EduUser> list = baseMapper.selectList(queryWrapper);
if(list.size()>0){
//在数据找到
return list.get(0);
}
return null;
}
}
登录controller层:
package com.czxy.zx.user.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.czxy.zx.domain.EduUser;
import com.czxy.zx.user.config.JwtProperties;
import com.czxy.zx.user.config.RabbitEmailConfig;
import com.czxy.zx.user.service.EduUserService;
import com.czxy.zx.utils.JwtUtils;
import com.czxy.zx.vo.BaseResult;
import com.czxy.zx.vo.UserEmail;
import com.google.gson.JsonObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/user")
public class EduUserController {
//redis相关实体类
@Resource
private StringRedisTemplate stringRedisTemplate;
//mq消息队列的实体类
@Resource
private RabbitTemplate rabbitTemplate;
@Resource
private EduUserService eduUserService;
@Resource
private JwtProperties jwtProperties;
@GetMapping("/info")
public BaseResult getInfo(String token) {
System.out.println("info-->"+token);
Map<String,Object> map = new HashMap<>();
try {
//使用jwt工具解析token得到用户, 获取用户的角色
EduUser user = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(), EduUser.class);
System.out.println("user = "+user);
if(user.getRoles()!=null){
map.put("roles", user.getRoles().split(","));
}else {
//为普通用户
map.put("roles",Arrays.asList("editor"));
}
map.put("avatar", "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
map.put("name",user.getUsername());
} catch (Exception e) {
e.printStackTrace();
return BaseResult.error("获取用户角色失败");
}
System.out.println(map);
return BaseResult.ok("登录成功",map);
}
@GetMapping("/logOut")
public BaseResult logOut(){
return BaseResult.ok("退出成功啦~");
}
@GetMapping("/active")
public BaseResult active(String uuid){
//1.从redis中获取该uuid 对应的用户名username,并从redis中删除
String username = stringRedisTemplate.opsForValue().get(uuid);
//2.校验
// username==null
if(username==null){
return BaseResult.error("超出激活时间, 或该用户已经成功激活");
}
//username如果存在 则去数据库中根据用户名获取该用户信息
QueryWrapper<EduUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",username);
EduUser one = eduUserService.getOne(queryWrapper);
//判断如果不存在该用户 则给出提示
if(one==null){
return BaseResult.error("该用户不存在");
}
//否则判断该用户是否要激活 1(激活过) 0(未激活)
if(!"0".equalsIgnoreCase(one.getStatus())){
return BaseResult.error("该用户已经激活过,无需再次激活");
}
//修改状态
one.setStatus("1");
eduUserService.updateById(one);
stringRedisTemplate.delete(uuid);
return BaseResult.ok("修改成功");
}
@PostMapping("/register")
public BaseResult register(@RequestBody EduUser user){
//先校验验证码是否正确
String vsCode = stringRedisTemplate.opsForValue().get("register_" + user.getUsername());
stringRedisTemplate.delete("register_"+user.getUsername());
if(vsCode==null){
return BaseResult.error("验证码已经失效l ");
}
if(!vsCode.equalsIgnoreCase(user.getVerifycode())){
return BaseResult.error("验证码错误");
}
boolean rel = eduUserService.register(user);
if(rel){
//成功 则随机生成一个uuid 并发送邮箱 需要用户激活
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
stringRedisTemplate.opsForValue().set(uuid,user.getUsername(),50, TimeUnit.MINUTES);
// String url="http://localhost:19528/#/active?uuid="+uuid;
String url="<a href='http://localhost:19528/#/active?uuid="+uuid+"'>点击激活<a/>";
String text=user.getUsername()+" 你好,您在使用本站的激活程序,请点击 "+url+" 进行激活";
//拼装路径
UserEmail userEmail = new UserEmail();
userEmail.setUsername(user.getUsername());
userEmail.setText(text);
userEmail.setEmail(user.getEmail());
//存放在消息队列中
String s = JSONObject.toJSONString(userEmail);
rabbitTemplate.convertAndSend(RabbitEmailConfig.QUEUE_REGISTER_EMAIL,s);
return BaseResult.ok("注册成功,去登录");
}else {
return BaseResult.error("注册失败啦,");
}
}
@PostMapping("/login")
public BaseResult login(@RequestBody EduUser user){
//1.校验验证码
//获取验证码
String vsCode = stringRedisTemplate.opsForValue().get("login_" + user.getUsername());
// 删除redis中的一次性验证码
stringRedisTemplate.delete("login_"+user.getUsername());
if(vsCode==null){
return BaseResult.error("验证码失效了");
}
if (!user.getVerifycode().equalsIgnoreCase(vsCode)){
return BaseResult.error("验证码错误");
}
//校验用户名和密码是否正确
EduUser loginU = eduUserService.login(user);
if(loginU!=null){
//String token="admin-token";
EduUser lu = new EduUser();//把当前用户非敏感信息作为token发送给前端
lu.setRoles(loginU.getRoles());
lu.setUsername(loginU.getUsername());
String token = JwtUtils.generateToken(lu, jwtProperties.getExpire(), jwtProperties.getPrivateKey());
System.out.println("token= "+token);
return BaseResult.ok("登录成功").append("token",token);
}else {
return BaseResult.error("用户名或者密码错误,登录失败");
}
}
@PostMapping("/sendEmail")
public BaseResult sendEmail(@RequestBody EduUser user){
//生成四位数的验证码
int number = new Random().nextInt(8999) + 1000;
//存入redis数据库中
stringRedisTemplate.opsForValue().set("login_"+user.getUsername(),number+"");
UserEmail userEmail = new UserEmail();
userEmail.setUsername(user.getUsername());
userEmail.setEmail(user.getEmail());
userEmail.setText("登录密码是:"+number);
//把验证码发送到消息队列中
String s = JSONObject.toJSONString(userEmail);
rabbitTemplate.convertAndSend(RabbitEmailConfig.QUEUE_LOGIN_EMAIL,s);
return BaseResult.ok("邮件发送中,请注意查阅");
}
}
package com.czxy.zx.user.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class EduHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("created",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("created",new Date(),metaObject);
}
}
验证码controller层:
package com.czxy.zx.user.controller;
import com.czxy.zx.utils.VerifyCodeUtils;
import com.czxy.zx.vo.BaseResult;
import com.czxy.zx.vo.VerifyCode;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RestController
@RequestMapping("/verifyCode")
public class VerifyCodeController {
@Resource
private StringRedisTemplate stringRedisTemplate;
@GetMapping("/{username}")
public void getVerifyCode(@PathVariable("username") String username, HttpServletResponse response) throws IOException {
VerifyCode verifyCode = VerifyCodeUtils.getVerifyCode();
stringRedisTemplate.opsForValue().set("register_"+username,verifyCode.getCode());
//给前端响应图片
ImageIO.write(verifyCode.getImage(),verifyCode.getType(),response.getOutputStream());
}
}
EduUser
package com.czxy.zx.domain;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.util.Date;
/**
*
*/
@Data
@TableName("edu_user")
@ApiModel(value = "EduUser对象",description = "用户")
public class EduUser {
@TableId(value="id" , type = IdType.AUTO)
private Integer id;
//用户名
private String username;
//用户密码
private String password;
//电话
private String phone;
//邮箱
private String email;
//角色,多个值使用逗号分隔,例如:admin,editor
private String roles;
//创建时间
@TableField(value = "created",fill = FieldFill.INSERT)
private Date created;
//状态:0 未激活、1已激活
private String status;
@TableField(exist = false)
private String verifycode;
@TableField(exist = false)
private String repassword;
}
更多推荐
所有评论(0)