添加了显示IP归属地的功能 #2

Closed
py3ni8q4o wants to merge 0 commits from main into master

@ -14,6 +14,7 @@
"element-plus": "^2.9.7",
"vee-validate": "^4.15.0",
"vue": "^3.5.13",
"vue-router": "^4.5.0",
"yup": "^1.6.1"
},
"devDependencies": {

@ -8,6 +8,9 @@ importers:
.:
dependencies:
'@vue/shared':
specifier: ^3.5.13
version: 3.5.13
axios:
specifier: ^1.8.3
version: 1.8.3
@ -20,6 +23,9 @@ importers:
vue:
specifier: ^3.5.13
version: 3.5.13(typescript@5.7.3)
vue-router:
specifier: ^4.5.0
version: 4.5.0(vue@3.5.13(typescript@5.7.3))
yup:
specifier: ^1.6.1
version: 1.6.1
@ -371,6 +377,9 @@ packages:
'@vue/compiler-vue2@2.7.16':
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
'@vue/devtools-api@6.6.4':
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
'@vue/devtools-api@7.7.2':
resolution: {integrity: sha512-1syn558KhyN+chO5SjlZIwJ8bV/bQ1nOVTG66t2RbG66ZGekyiYNmRO7X9BJCXQqPsFHlnksqvPhce2qpzxFnA==}
@ -733,6 +742,11 @@ packages:
'@vue/composition-api':
optional: true
vue-router@4.5.0:
resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==}
peerDependencies:
vue: ^3.2.0
vue-tsc@2.2.8:
resolution: {integrity: sha512-jBYKBNFADTN+L+MdesNX/TB3XuDSyaWynKMDgR+yCSln0GQ9Tfb7JS2lr46s2LiFUT1WsmfWsSvIElyxzOPqcQ==}
hasBin: true
@ -980,6 +994,8 @@ snapshots:
de-indent: 1.0.2
he: 1.2.0
'@vue/devtools-api@6.6.4': {}
'@vue/devtools-api@7.7.2':
dependencies:
'@vue/devtools-kit': 7.7.2
@ -1345,6 +1361,11 @@ snapshots:
dependencies:
vue: 3.5.13(typescript@5.7.3)
vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)):
dependencies:
'@vue/devtools-api': 6.6.4
vue: 3.5.13(typescript@5.7.3)
vue-tsc@2.2.8(typescript@5.7.3):
dependencies:
'@volar/typescript': 2.4.12

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -1,10 +1,9 @@
<script setup lang="ts">
import LogPage from './components/LogPage.vue';
</script>
<template>
<LogPage/>
<router-view/>
</template>
<style scoped>

Before

Width:  |  Height:  |  Size: 496 B

After

Width:  |  Height:  |  Size: 496 B

@ -1,51 +1,49 @@
<script setup lang="ts">
import request from '../utils/request' //
import * as yup from 'yup'
import { useField, useForm } from 'vee-validate'
import { ref ,watch} from 'vue'
import {useRouter} from 'vue-router'
import { useField, useForm, } from 'vee-validate'
import { ref } from 'vue'
import { computed } from 'vue'
const token = ref<string>('')
const router = useRouter()
//
const testEmail = ref('test@example.com')
const testPassword = ref('123456')
//
const transRate = ref('90')
const switchLogin = ref(true);
const switchLoginMethod = ref<boolean>(true);
const switchLoginMethodEvent = () =>{
password.value = vericode.value = verifyPassword.value = "";
switchLoginMethod.value = !switchLoginMethod.value;
if(switchLoginMethod.value)//
{
password.value = verifyPassword.value = 123456;
}
else
{
vericode.value = 1;
if(switchLoginMethod.value) {
login_password.value = "";
login_password_email.value = login_vericode_email.value;
} else {
login_vericode.value = "";
login_vericode_email.value = login_password_email.value;
}
}
const form_box = ref<HTMLElement | null>(null);
//
const switchLoginEvent = () =>{
password.value = vericode.value = verifyPassword.value = "";
if(switchLogin.value)//
{
if(switchLoginMethod.value)//
{
password.value = verifyPassword.value = 123456;
}
else
{
vericode.value = 1;
}
}
//
const switchLoginEvent = () =>{
switchLogin.value = !switchLogin.value;
switchLogin.value ? transRate.value = '0' : transRate.value = '90';
if(form_box.value)
{
form_box.value.style.transform = `translateX(${transRate.value}%)`;
}
if(switchLoginMethod.value) {
login_password.value = login_vericode.value = "";
login_password_email.value = login_vericode_email.value = register_email.value;
} else {
register_password.value = register_verifyPassword.value = register_vericode.value = "";
login_vericode_email.value = login_password_email.value = register_email.value;
}
}
@ -53,56 +51,68 @@ const form_box = ref<HTMLElement | null>(null);
//
const RegisterForm = useForm({
validationSchema : yup.object({
email: yup.string().email("请输入邮箱").required("请输入正确的邮箱"),
password: yup.string().min(6,"密码至少6位").required("请输入密码"),
verifyPassword: yup.string().oneOf([yup.ref('password')],"两次密码不一致").required("请确认密码"),
vericode: yup.string().required("请输入验证码")
register_email: yup.string().email("请输入邮箱").required("请输入正确的邮箱"),
register_password: yup.string().min(6,"密码至少6位").required("请输入密码"),
register_verifyPassword: yup.string().oneOf([yup.ref('register_verifyPassword')],"两次密码不一致").required("请确认密码"),
register_vericode: yup.string().required("请输入验证码")
}),
})
const {value: register_email} = useField('register_email')
const {value: register_password} = useField ('register_password')
const {value: register_verifyPassword} = useField('register_verifyPassword')
const {value: register_vericode} = useField('register_vericode')//
//
//
const LoginPasswordForm = useForm({
validationSchema : yup.object({
email: yup.string().email("请输入邮箱").required("请输入正确的邮箱"),
password: yup.string().min(6,"密码至少6位").required("请输入密码"),
login_password_email: yup.string().email("请输入邮箱").required("请输入正确的邮箱"),
login_password: yup.string().min(6,"密码至少6位").required("请输入密码")
}),
})
const{value: login_password_email} = useField('login_password_email')
const{value: login_password} = useField('login_password')
//
const LoginEmailForm = useForm({
validationSchema : yup.object({
email: yup.string().email("请输入邮箱").required("请输入正确的邮箱"),
vericode: yup.string().required("请输入验证码")
login_vericode_email: yup.string().email("请输入邮箱").required("请输入正确的邮箱"),
login_vericode: yup.string().required("请输入验证码")
}),
})
const {value: email} = useField('email')
const {value: password} = useField('password')
const {value: verifyPassword} = useField('verifyPassword')
const {value: vericode} = useField('vericode')//
const{value:login_vericode_email} = useField('login_vericode_email')
const{value: login_vericode} = useField('login_vericode')
//
const showErrors = ref(false)
const ErrorsMessage = ref('')
const checkErrors = () => {
if(RegisterForm.errors.value.email || RegisterForm.errors.value.password || RegisterForm.errors.value.verifyPassword || RegisterForm.errors.value.vericode) {
if(RegisterForm.errors.value.register_email || RegisterForm.errors.value.register_password || RegisterForm.errors.value.register_verifyPassword || RegisterForm.errors.value.register_vericode) {
showErrors.value = true
ErrorsMessage.value = '请检查输入是否正确'
}
if(LoginPasswordForm.errors.value.email || LoginPasswordForm.errors.value.password) {
}else if(LoginPasswordForm.errors.value.login_password_email || LoginPasswordForm.errors.value.login_password) {
showErrors.value = true
ErrorsMessage.value = '请检查输入是否正确'
}
if(LoginEmailForm.errors.value.email || LoginEmailForm.errors.value.vericode) {
}else if(LoginEmailForm.errors.value.login_vericode_email || LoginEmailForm.errors.value.login_vericode) {
showErrors.value = true
ErrorsMessage.value = '请检查输入是否正确'
}
}
//
const onRegisterSubmit = () => {
RegisterForm.handleSubmit(() => {
LoginEmailForm.resetForm();
LoginPasswordForm.resetForm();
console.log('函数调用成功')
RegisterForm.handleSubmit((values) => {
console.log('表单调用成功',values)
testcode().then((res) => {
if(res.code === 200) {
register().then((res) => {
if(res.code === 200) {
console.log('注册成功')
localStorage.setItem('token', res.data.token)
router.push({path:'/personal'})
} else {
console.log('注册失败')
}
@ -111,34 +121,63 @@ const onRegisterSubmit = () => {
console.log('注册失败')
}
})
},
(errors)=>{
console.log('注册表单调用失败',errors)
})();
}
const chooseLogin = () => {
//
const onLoginSubmit = () => {
if(!switchLoginMethod.value) {
onLoginPasswordSubmit()
} else {
onLoginEmailSubmit()
}
}
const onLoginPasswordSubmit = () => {
LoginPasswordForm.handleSubmit(() => {
LoginEmailForm.resetForm();
RegisterForm.resetForm();
LoginPasswordForm.handleSubmit((values) => {
console.log('表单调用成功',values)
if(login_password_email.value == testEmail.value && login_password.value == testPassword.value) {
console.log('测试登录成功')
console.log(router)
router.push({name:'Personal'})
} else {
login().then((res) => {
if(res.code === 200) {
console.log('登录成功')
console.log('密码登录成功')
localStorage.setItem('token', res.data.token)
} else {
console.log('登录失败')
}
})
}
},
(errors) => {
console.log('密码登录表单调用失败',errors)
})();
}else {
LoginPasswordForm.resetForm();
RegisterForm.resetForm();
LoginEmailForm.handleSubmit(() => {
testcode().then((res) => {
if(res.code === 200) {
console.log('邮箱登录成功')
localStorage.setItem('token', res.data.token)
} else {
console.log('登录失败')
}
})
},
(errors)=>{
console.log("邮箱登录表单调用失败",errors)
})();
}
}
const onLoginEmailSubmit = () => {
LoginEmailForm.handleSubmit(() => {
testcode().then((res) => {
if(res.code === 200) {
console.log('登录成功')
localStorage.setItem('token', res.data.token)
} else {
console.log('登录失败')
}
@ -147,6 +186,9 @@ const onLoginEmailSubmit = () => {
}
//axios
const email = computed(()=>register_email.value ?? login_password_email.value ?? login_vericode_email.value)
const password = computed(()=>register_password.value ?? login_password.value)
const vericode = computed(()=>register_vericode.value ?? login_vericode.value)
//
async function emailcode(){
const res = await request.get('/users/code', {
@ -195,49 +237,54 @@ async function login(){
})
return res.data;
}
</script>
<template>
<title>登录</title>
<!-- 错误信息显示但是有bug无法显示 -->
<el-alert
class = "error-msg"
v-if="showErrors"
title={{ ErrorsMessage }}
:title=ErrorsMessage
type="error"
:closable="true"
@close="showErrors = false"
show-icon = "true"
center/>
<!-- 登录注册表单 -->
<div class = "container">
<div ref = "form_box" class = "form-box">
<!-- 注册表单 -->
<div class = "register-box" :class = "{hidden: !switchLogin}">
<h1>register</h1>
<div class="email-vericode">
<input type="text" placeholder="邮箱" v-model="email">
<input type="text" placeholder="邮箱" v-model="register_email">
<button class="vericode-btn" @click = "emailcode">获取验证码</button>
</div>
<input type="password" placeholder="密码" v-model="password">
<input type ="password" placeholder = "确认密码" v-model = "verifyPassword">
<input type="text" placeholder="验证码" v-model="vericode">
<button @click = "onRegisterSubmit;checkErrors">注册</button>
<input type="password" placeholder="密码" v-model="register_password">
<input type ="password" placeholder = "确认密码" v-model = "register_verifyPassword">
<input type="text" placeholder="验证码" v-model="register_vericode">
<button @click = "onRegisterSubmit(),checkErrors()">注册</button>
</div>
<!-- 登录表单 -->
<div class = "login-box" :class ="{hidden: switchLogin}">
<!--密码登录-->
<div class = password-method :class="{hidden: switchLoginMethod}">
<h1>login</h1>
<input type="text" placeholder="邮箱" v-model="email">
<input type="password" placeholder="密码" v-model="password">
<input type="text" placeholder="邮箱" v-model="login_password_email">
<input type="password" placeholder="密码" v-model="login_password">
<button class = "switch-btn" @click="switchLoginMethodEvent"></button>
</div>
<!--邮箱登录-->
<div class = "email-method" :class ="{hidden: !switchLoginMethod}">
<h1>login</h1>
<input class ="email-text" type="text" placeholder="邮箱" v-model="email">
<input class ="email-text" type="text" placeholder="邮箱" v-model="login_vericode_email">
<button class="email-btn" @click="emailcode"></button>
<input type="text" placeholder="验证码" v-model="vericode">
<input type="text" placeholder="验证码" v-model="login_vericode">
<button class = "switch-btn" @click="switchLoginMethodEvent"></button>
</div>
<button @click="chooseLogin;checkErrors"></button>
<button @click="onLoginSubmit(),checkErrors()"></button>
</div>
</div>
<div>
@ -288,14 +335,12 @@ async function login(){
position: relative;
}
.error-msg p{
align-items: center;
position:absolute;
flex-direction: column;
color:#fff;
font-size:20px;
top:12%;
left:37%;
.error-msg{
width:30%;
position: absolute;
top:5%;
left:35%;
display: flex;
}
.form-box{

@ -0,0 +1,201 @@
<script set lang="ts">
import { defineComponent,ref } from 'vue';
export default defineComponent({
name: 'Personal',
setup(){
// `li`
const activeIndex = ref<number>(0);
// `active`
const setActive = (index: number) => {
activeIndex.value = index; // Vue `active`
};
return {
activeIndex,
setActive
};
}
});
</script>
<template>
<div class = "shell">
<ul class="nav">
<li :class="{active: activeIndex == 0}" @click ="setActive(0)" id = "avatar">
<router-link :to="{name:'Personal'}">
<div class="icon">
<div class="imageBox">
<img src="../../public/images/默认头像.jpg">
</div>
</div>
<div class="text">测试样例</div>
</router-link>
</li>
<li :class="{active:activeIndex === 1}" @click="setActive(1)">
<router-link :to="{name:'Manager'}">
<div class="icon">
<div class="imageBox">
<img src="../../public/images/个人.png">
</div>
</div>
<div class="text">账号管理</div>
</router-link>
</li>
</ul>
</div>
</template>
<style scoped>
*{
margin:0;
padding:0;
box-sizing : border-box;
list-style:none;
text-decoration:none;
}
section{
position:relative;
width:100%;
height:100vh;
display:flex;
justify-content:center;
align-items:center;
font:900 100px '';
color:rgba(110,90,240,0.3);
background-color: #e4e9f5;
}
.shell{
position:absolute;
top:0%;
left:0%;
width:84px;
height:100%;
background-color:#fff;
z-index:9999;
transition:width 0.5s;
padding-left:10px;
overflow:hidden;
}
.shell:hover{
width:300px;
}
.imageBox{
position:relative;
width:50px;
height:50px;
border-radius:50%;
overflow:hidden;
}
.imageBox img{
width:100%;
height:100%;
object-fit:cover;
}
.shell ul{
position:relative;
height:100vh;
}
.shell ul li{
position:relative;
padding:5px;
}
.active{
background-color: #e4e9f5;
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
}
.active::before{
content:"";
position:absolute;
top:-30px;
right:0;
width:30px;
height:30px;
border-bottom-right-radius:25px;
box-shadow:5px 5px 0 5px #e4e9f5;
background:transparent;
}
.active::after{
content:"";
position:absolute;
bottom:-30px;
right:0;
width:30px;
height:30px;
border-top-right-radius: 25px;
box-shadow:5px -5px 0 5px #e4e9f5;
background:transparent;
}
#avatar{
margin:40px 0 100px 0;
}
.shell ul li a{
position:relative;
display:flex;
white-space:nowrap;
}
.icon{
position:relative;
display:flex;
justify-content:center;
align-items: center;
min-width:60px;
padding-left:10px;
height:70px;
color:#333;
transition:0.5s;
color: rgb(110,90,240)
}
.icon i{
font-size:30px;
z-index:999;
}
.text{
position:relative;
height:70px;
display:flex;
align-items:center;
font-size:20px;
color:#333; /*字体颜色 */
padding-left:15px;
text-transform:uppercase;
letter-spacing:2px;/*字体间距*/
transition:0.5s;
}
.shell ul li:hover a .icon,
.shell ul li:hover a .text
{
color: #ffa117;/*字体和图标被选中后的颜色 */
}
.active a .icon::before{
content:"";
position:absolute;
inset:5px;
width:60px;
background:#fff;
border-radius:50%;
transition:0.5s;
border:7px solid rgb(110,90,240);
box-sizing:border-box;
}
</style>

@ -0,0 +1,14 @@
<script set lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'Manager',
});
</script>
<template>
</template>
<sytle scoped>
</sytle>

@ -3,7 +3,10 @@ import './style.css'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import router from './routers'
const app = createApp(App)
app.mount('#app')
app.use(ElementPlus)
app.use(router)
app.mount('#app')

@ -0,0 +1,32 @@
import type { RouteRecord, RouteRecordRaw } from 'vue-router';
import { createWebHashHistory, createRouter,createWebHistory } from 'vue-router';
import LogPage from './components/LogPage.vue';
import Personal from './components/Personal.vue';
import Manager from './components/Personal/AcountManager.vue';
const routes:Array<RouteRecordRaw> = [
{
path:'/log',
name: 'LogPage',
component: LogPage
},
{
path:'/personal',
name: 'Personal',
component: Personal,
children: [
{
path:'manager',
name: 'Manager',
component:Manager,
}
]
}
];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
});
export default router;

@ -45,4 +45,11 @@ pnpm install yup
```cmd
pnpm add element-plus
```
组件
### 安装VueRouter
```cmd
pnpm i vue-router
pnpm add vue-router@4
```
用来完成界面跳转同时完成vuerouter与ts的适配

@ -35,6 +35,13 @@
<version>1.18.36</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@ -50,29 +57,22 @@
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
<!-- jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.1.3</version>
</dependency>
<!-- 邮箱服务-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- IP地址服务-->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
<dependencyManagement>

@ -40,10 +40,9 @@ public class Result<T>{
}
public static <T> Result<T> success(T data, String message) {
return new Result<>(200, message, data);
return new Result<>(200, message, null);
}
/**
*
* @param code

@ -0,0 +1,40 @@
package com.unilife.config;
import com.unilife.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private JwtInterceptor jwtInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor).addPathPatterns("/**")
.excludePathPatterns(
"/users/login",
"/users/register",
"/users/code",
"/users/login/code",
"/swagger-resources/**",
"/v2/api-docs/**",
"/doc.html",
"/webjars/**"
);
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*") // 允许所有来源,生产环境建议限制为特定域名
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}

@ -2,20 +2,26 @@ package com.unilife.controller;
import com.unilife.common.result.Result;
import com.unilife.model.dto.EmailDTO;
import com.unilife.model.dto.LogDTO;
import com.unilife.model.dto.LoginDTO;
import com.unilife.model.dto.LoginEmailDTO;
import com.unilife.model.dto.RegisterDTO;
import com.unilife.model.vo.LoginVO;
import com.unilife.model.vo.RegisterVO;
import com.unilife.service.UserService;
import com.unilife.utils.BaseContext;
import com.unilife.utils.JwtUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
@Api(tags = "用户管理")
@ -26,16 +32,34 @@ public class UserController {
@Autowired
private UserService userService;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@ApiOperation(value = "用户注册")
@PostMapping("register")
public Result register(@RequestBody LoginDTO loginDTO, HttpServletRequest request) {
return userService.register(loginDTO,request);
public Result register(@RequestBody RegisterDTO registerDTO, HttpServletRequest request) {
return userService.register(registerDTO,request);
}
@ApiOperation(value = "用户登录")
@PostMapping("login")
public Result login(@RequestBody LogDTO logDTO,HttpServletRequest request) { return userService.login(logDTO,request); }
public Result login(@RequestBody LoginDTO loginDTO,HttpServletRequest request) {
Result login = userService.login(loginDTO,request);
//登陆成功后生成jwt令牌
LoginVO vo=(LoginVO) login.getData();
if (vo == null) {
return login;
}
Long id = vo.getId();
String token = jwtUtil.generateToken(id);
vo.setToken(token);
//Threadlocal保存当前用户id
BaseContext.setId(id);
return Result.success(vo);
}
@ApiOperation(value = "获取邮箱验证码")
@PostMapping("code")

@ -0,0 +1,54 @@
package com.unilife.interceptor;
import cn.hutool.core.util.StrUtil;
import com.unilife.utils.BaseContext;
import com.unilife.utils.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("JwtInterceptor preHandle");
String token = request.getHeader("Authorization");
if(StrUtil.isBlank(token)){
response.setStatus(401);
return false;
}
boolean verified = jwtUtil.verifyToken(token);
if (!verified) {
response.setStatus(401);
return false;
}
//从token中获取userid并存入threadlocal
Long userId = jwtUtil.getUserIdFromToken(token);
BaseContext.setId(userId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
BaseContext.removeId();
}
}

@ -8,7 +8,7 @@ import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LogDTO {
public class LoginDTO {
private String email;
private String password;
}

@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginDTO {
public class RegisterDTO {
private String username;
private String email;
private String password;

@ -0,0 +1,21 @@
package com.unilife.model.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginVO {
private Long id;
private String username;
private String nickname;
private String avatar;
private Byte role;
private Byte isVerified;
private Byte status;
private String token;
private String LoginIp;
}

@ -0,0 +1,15 @@
package com.unilife.model.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RegisterVO {
private Long id;
private String username;
private String nickname;
private String loginIp;
}

@ -1,5 +1,4 @@
package com.unilife.service;
import com.unilife.model.dto.LoginDTO;
import javax.servlet.http.HttpServletRequest;

@ -1,22 +1,19 @@
package com.unilife.service;
import com.unilife.common.result.Result;
import com.unilife.model.dto.LogDTO;
import com.unilife.model.dto.LoginDTO;
import com.unilife.model.dto.LoginEmailDTO;
import com.unilife.model.dto.RegisterDTO;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
public interface UserService {
Result register(LoginDTO loginDTO, HttpServletRequest request);
Result register(RegisterDTO registerDTO, HttpServletRequest request);
Result login(LogDTO logDTO, HttpServletRequest request);
Result login(LoginDTO loginDTO,HttpServletRequest request);
Result sendVerificationCode(String email,HttpServletRequest request);
Result loginWithEmail(LoginEmailDTO loginEmailDTO, HttpServletRequest request);
Result loginWithEmail(LoginEmailDTO loginEmailDTO,HttpServletRequest request);
}

@ -2,18 +2,18 @@ package com.unilife.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.unilife.common.constant.RedisConstant;
import com.unilife.common.result.Result;
import com.unilife.mapper.UserMapper;
import com.unilife.model.dto.LogDTO;
import com.unilife.model.dto.LoginDTO;
import com.unilife.model.dto.LoginEmailDTO;
import com.unilife.model.dto.RegisterDTO;
import com.unilife.model.entity.User;
import com.unilife.model.vo.LogVO;
import com.unilife.model.vo.LoginVO;
import com.unilife.model.vo.RegisterVO;
import com.unilife.service.IPLocationService;
import com.unilife.service.UserService;
import com.unilife.utils.JwtUtil;
import com.unilife.utils.RegexUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -29,7 +29,6 @@ import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@ -39,6 +38,7 @@ import static com.unilife.common.constant.RedisConstant.LOGIN_EMAIL_KEY;
@Component
@Service
public class UserServiceImpl implements UserService {
@Autowired
private IPLocationService ipLocationService;
@ -56,53 +56,53 @@ public class UserServiceImpl implements UserService {
final int CODE_EXPIRE_MINUTES = 10;
final int LIMIT_SECONDS=60;
@Autowired
private JwtUtil jwtUtil;
@Override
public Result register(LoginDTO loginDTO, HttpServletRequest request) {
if(loginDTO.getEmail().isEmpty() || loginDTO.getPassword().isEmpty()) {
public Result register(RegisterDTO registerDTO, HttpServletRequest request) {
if(registerDTO.getEmail().isEmpty() || registerDTO.getPassword().isEmpty()) {
return Result.error(400,"邮箱或密码不能为空");
}
if(loginDTO.getPassword().length() < 6) {
if(registerDTO.getPassword().length() < 6) {
return Result.error(400,"密码长度过短!");
}
User getuser = userMapper.FindByOnlyEmail(loginDTO.getEmail());
User getuser = userMapper.FindByOnlyEmail(registerDTO.getEmail());
if(getuser != null) {
return Result.error(400,"用户已存在!");
}
User user = new User();
BeanUtil.copyProperties(loginDTO,user);
BeanUtil.copyProperties(registerDTO,user);
String IPAddress = ipLocationService.getClientIP(request);
String Location = ipLocationService.getIPLocation(IPAddress);
user.setLoginIp(Location);
userMapper.insert(user);
LoginVO loginVO = new LoginVO(Math.toIntExact(user.getId()),user.getUsername()
RegisterVO registerVO = new RegisterVO(user.getId(),user.getUsername()
,user.getNickname(),user.getLoginIp());
return Result.success(loginVO);
return Result.success(registerVO);
}
@Override
public Result login(LogDTO logDTO,HttpServletRequest request) {
public Result login(LoginDTO loginDTO,HttpServletRequest request) {
User user = new User();
BeanUtil.copyProperties(logDTO,user);//将登录的前端传来的消息拷贝给这个user
BeanUtil.copyProperties(loginDTO,user);//将登录的前端传来的消息拷贝给这个user
User getuser = userMapper.FindByEmail(user.getEmail(),user.getPassword());
if(getuser == null)
{
return Result.error(logDTO,"用户不存在,登录失败!");
return Result.error(loginDTO,"用户不存在,登录失败!");
}
if(!user.getPassword().equals(getuser.getPassword()))
{
return Result.error(logDTO,"密码错误,登录失败!");
return Result.error(loginDTO,"密码错误,登录失败!");
}
String LastLogIpLocation = getuser.getLoginIp();
String IPAddress = ipLocationService.getClientIP(request);
String Location = ipLocationService.getIPLocation(IPAddress);
getuser.setLoginIp(Location);
userMapper.UpdateIPLocation(getuser.getEmail(), getuser.getLoginIp());
LogVO logVO = new LogVO(Math.toIntExact(getuser.getId()), getuser.getUsername(), getuser.getNickname(),
getuser.getAvatar(), getuser.getRole(), getuser.getIsVerified(), getuser.getStatus(),getuser.getLoginIp());
return Result.success(logVO,"上次登录IP归属地为" + LastLogIpLocation);
LoginVO loginVO = new LoginVO();
return Result.success(loginVO,"上次登录IP归属地为" + LastLogIpLocation);
}
@Override
@ -202,9 +202,10 @@ public class UserServiceImpl implements UserService {
//6.生成登录凭证
//TODO
String token = jwtUtil.generateToken(user.getId());
// 8. 返回用户信息和登录凭证
Map<String, Object> userInfo = new HashMap<>();
//HashMap userInfo.put("token", token);
userInfo.put("token", token);
userInfo.put("user", user);
return Result.success(userInfo);

@ -0,0 +1,19 @@
package com.unilife.utils;
public class BaseContext {
public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setId(Long id) {
threadLocal.set(id);
}
public static Long getId() {
return threadLocal.get();
}
public static void removeId() {
threadLocal.remove();
}
}

@ -0,0 +1,45 @@
package com.unilife.utils;
import cn.hutool.core.date.DateTime;
import cn.hutool.jwt.JWTUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
public long expiration;
public String generateToken(Long id) {
DateTime now = DateTime.now();
DateTime expireTime = new DateTime(now.getTime() + expiration * 1000);
Map<String, Object> payload = new HashMap<>();
payload.put("userId", id);
payload.put("created",now.getTime());
return JWTUtil.createToken(payload,secret.getBytes());
}
public boolean verifyToken(String token) {
try{
JWTUtil.verify(token,secret.getBytes());
return true;
}catch (Exception e){
return false;
}
}
public Long getUserIdFromToken(String token) {
try {
return (Long)JWTUtil.parseToken(token).getPayload("userId");
}catch (Exception e){
return null;
}
}
}

@ -1,9 +1,8 @@
server:
port: 8087
forward-headers-strategy: framework
spring:
datasource:
url: jdbc:mysql://localhost:3306/UniLife?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
url: jdbc:mysql://localhost:3306/UniLife?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
username: root
password: zhong20050428
driver-class-name: com.mysql.cj.jdbc.Driver
@ -24,10 +23,6 @@ spring:
redis:
host: 127.0.0.1
port: 6379
#电脑上要下载redis客户端
#每次使用发送邮件功能前需要在redis目录下启动cmd并执行redis-server.exe redis.windows.conf打开服务
#若出现一个盒子状的东西和port参数服务启动成功此时不要关闭命令行窗口否则会关闭redis服务
#只有redis服务启动成功邮件才能正常发送
knife4j:
enable: true
openapi:
@ -52,4 +47,7 @@ mybatis:
map-underscore-to-camel-case: true
logging:
level:
com.unilife: debug
com.unilife: debug
jwt:
secret: qwertyuiopasdfghjklzxcvbnm
expiration: 86400

@ -1,37 +0,0 @@
import request from '../utils/request';
// 用户注册
export function register(data) {
return request({
url: '/auth/register',
method: 'post',
data
});
}
// 用户密码登录
export function login(data) {
return request({
url: '/auth/login',
method: 'post',
data
});
}
// 获取邮箱验证码
export function getEmailCode(data) {
return request({
url: '/auth/email/code',
method: 'post',
data
});
}
// 邮箱验证码登录
export function loginWithCode(data) {
return request({
url: '/auth/login/code',
method: 'post',
data
});
}

@ -1,30 +0,0 @@
package com.unilife.model.vo;
import com.unilife.model.entity.User;
import lombok.Data;
@Data
public class LogVO {
private Integer id;
private String username;
private String nickname;
private String avatar;
private Byte role;
private Byte isVerified;
private Byte status;
private String loginIp;
public LogVO(Integer id,String username,String nickname,String avatar,Byte role,Byte isVerified,Byte status,String loginIp)
{
this.id = id;
this.username = username;
this.nickname = nickname;
this.avatar = avatar;
this.role = role;
this.isVerified = isVerified;
this.status = status;
this.loginIp = loginIp;
}
}

@ -1,19 +0,0 @@
package com.unilife.model.vo;
import lombok.Data;
@Data
public class LoginVO {
private Integer id;
private String username;
private String nickname;
private String loginIp;
public LoginVO(Integer id, String username, String nickname,String loginIp)
{
this.id = id;
this.username = username;
this.nickname = nickname;
this.loginIp = loginIp;
}
}

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 268 KiB

Loading…
Cancel
Save