Login + getInfo + Register 后端 API 书写, login + logout 前端书写
This commit is contained in:
parent
78db9a28c8
commit
0badc0f83d
|
@ -68,6 +68,30 @@
|
|||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 用于使用 JWT 验证-->
|
||||
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -83,6 +107,12 @@
|
|||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>13.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -1,17 +1,55 @@
|
|||
package com.kob.backend.config;
|
||||
|
||||
/*
|
||||
主要作用:放行登录、注册等接口
|
||||
*/
|
||||
|
||||
import com.kob.backend.config.filter.JwtAuthenticationTokenFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
//明文加密
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
// TODO: 2023/2/19 WebSecurityConfigurerAdapter 已被弃用,注意替换
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
|
||||
|
||||
// 配置密码加密方式
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
/*
|
||||
.antMatchers("/user/account/token/", "/user/account/register/").permitAll()
|
||||
用于配置公开链接
|
||||
*/
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/user/account/token/", "/user/account/register/").permitAll()
|
||||
.antMatchers(HttpMethod.OPTIONS).permitAll()
|
||||
.anyRequest().authenticated();
|
||||
|
||||
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package com.kob.backend.config.filter;
|
||||
|
||||
//filter 类用于验证
|
||||
/*
|
||||
主要作用:
|
||||
1. 用来验证jwt token,如果验证成功,则将User信息注入上下文中
|
||||
*/
|
||||
|
||||
import com.kob.backend.mapper.UserMapper;
|
||||
import com.kob.backend.pojo.User;
|
||||
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||
import com.kob.backend.utils.JwtUtil;
|
||||
import com.sun.xml.internal.bind.v2.TODO;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
|
||||
String token = request.getHeader("Authorization");
|
||||
|
||||
// TODO 这里可以更改 bearar 为其他字符作为验证前缀
|
||||
if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
token = token.substring(7);
|
||||
|
||||
String userid;
|
||||
try {
|
||||
Claims claims = JwtUtil.parseJWT(token);
|
||||
userid = claims.getSubject();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
User user = userMapper.selectById(Integer.parseInt(userid));
|
||||
|
||||
if (user == null) {
|
||||
throw new RuntimeException("用户名未登录");
|
||||
}
|
||||
|
||||
UserDetailsImpl loginUser = new UserDetailsImpl(user);
|
||||
UsernamePasswordAuthenticationToken authenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(loginUser, null, null);
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
|
@ -12,7 +12,8 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
//实现对用户类 User 的增删查操作
|
||||
//实现对用户类 User 的增删查操作->这个类仅用于调试,故 Deparecated 掉
|
||||
@Deprecated
|
||||
@RestController
|
||||
public class UserController {
|
||||
|
||||
|
@ -58,7 +59,7 @@ public class UserController {
|
|||
// 加密明文密码并存入密文到数据库
|
||||
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
String encodedPassword = passwordEncoder.encode(password);
|
||||
User user = new User(userId, username, encodedPassword);
|
||||
User user = new User(userId, username, encodedPassword,"");
|
||||
userMapper.insert(user);
|
||||
return "Add User Successfully";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package com.kob.backend.controller.user.account;
|
||||
|
||||
import com.kob.backend.service.user.account.InfoService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
public class InfoController {
|
||||
@Autowired
|
||||
private InfoService infoService;
|
||||
|
||||
// 获取信息
|
||||
@GetMapping("/user/account/info/")
|
||||
public Map<String,String> getInfo(){
|
||||
return infoService.getInfo();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.kob.backend.controller.user.account;
|
||||
|
||||
import com.kob.backend.service.user.account.LoginService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
//@RestController 配合 @PostMapping("/.../.../") 使用:映射 Url 地址
|
||||
@RestController
|
||||
public class LoginController {
|
||||
// @Autowired 注入接口
|
||||
@Autowired
|
||||
private LoginService loginService;
|
||||
|
||||
// 登录使用 POST 请求,密文传输,更安全
|
||||
@PostMapping("/user/account/token/")
|
||||
public Map<String,String> getToken(@RequestParam Map<String,String> map){
|
||||
String username = map.get("username");
|
||||
String password = map.get("password");
|
||||
|
||||
return loginService.getToken(username,password);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.kob.backend.controller.user.account;
|
||||
|
||||
import com.kob.backend.service.user.account.RegisterService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
public class RegisterController {
|
||||
@Autowired
|
||||
private RegisterService registerService;
|
||||
|
||||
@PostMapping("/user/account/register/")
|
||||
public Map<String, String> register(@RequestParam Map<String, String> map) {
|
||||
String username = map.get("username");
|
||||
String password = map.get("password");
|
||||
String confirmedPassword = map.get("confirmedPassword");
|
||||
return registerService.register(username, password, confirmedPassword);
|
||||
}
|
||||
|
||||
}
|
|
@ -6,6 +6,8 @@ Data 用于编译时自动生成 getter setter 方法;
|
|||
NoArgsConstructor 用于生成无参构造函数;
|
||||
AllArgsConstructor 用于生成所有参数构造函数.
|
||||
*/
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
@ -16,8 +18,11 @@ import lombok.NoArgsConstructor;
|
|||
public class User {
|
||||
|
||||
// 使用对象类型定义而不是 Int 防止 Mybatis 报错
|
||||
// @TableId(type = IdType.AUTO) 用于 id 的自增
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Integer id;
|
||||
private String username;
|
||||
private String password;
|
||||
private String photo;
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,12 @@ public class UserDetailsServiceImpl implements UserDetailsService {
|
|||
private UserMapper userMapper;
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
/*
|
||||
从数据库里读取用户信息,比对用户名密码与数据库存在的用户名密码,信息一致,给用户发一个 sessionID ,
|
||||
存到用户本地浏览器,服务器同时会自己存一份。未来用户再登录时服务器会将自己的 sessionID 与用户发过来
|
||||
请求包含的 sessionID 进行比照,如果一致,则授权成功。
|
||||
*/
|
||||
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("username",username);
|
||||
User user = userMapper.selectOne(queryWrapper);
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package com.kob.backend.service.impl.user.account;
|
||||
|
||||
import com.kob.backend.pojo.User;
|
||||
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||
import com.kob.backend.service.user.account.InfoService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Service
|
||||
public class InfoServiceImpl implements InfoService {
|
||||
@Override
|
||||
public Map<String, String> getInfo() {
|
||||
UsernamePasswordAuthenticationToken authenticationToken =
|
||||
(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
UserDetailsImpl loginUser = (UserDetailsImpl) authenticationToken.getPrincipal();
|
||||
User user = loginUser.getUser();
|
||||
|
||||
Map<String,String> map = new HashMap<>();
|
||||
map.put("error_message","success");
|
||||
map.put("id",user.getId().toString());
|
||||
map.put("username",user.getUsername());
|
||||
map.put("photo",user.getPhoto());
|
||||
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package com.kob.backend.service.impl.user.account;
|
||||
|
||||
import com.kob.backend.pojo.User;
|
||||
import com.kob.backend.service.impl.utils.UserDetailsImpl;
|
||||
import com.kob.backend.service.user.account.LoginService;
|
||||
import com.kob.backend.utils.JwtUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
//@Service 注解用于注入接口实现到 Spring 里面
|
||||
@Service
|
||||
public class LoginServiceImpl implements LoginService {
|
||||
|
||||
// @Autowired 用于注入
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Override
|
||||
public Map<String, String> getToken(String username, String password) {
|
||||
|
||||
// 封装用户名和密码成 UsernamePasswordAuthenticationToken 类:此类不存储明文,而是存储加密之后的字符串
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,password);
|
||||
|
||||
// 验证是否正常登录,登录验证失败时会自动处理(报异常)
|
||||
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
|
||||
// 登录成功,取出用户
|
||||
UserDetailsImpl loginUser = (UserDetailsImpl) authenticate.getPrincipal();
|
||||
User user = loginUser.getUser();
|
||||
|
||||
// 封装 jwt 信息:将 userID 转换为 String
|
||||
String jwt = JwtUtil.createJWT(user.getId().toString());
|
||||
|
||||
/*
|
||||
成功之后返回结果:
|
||||
1. "error_message" 为 success;失败之后会报异常,自动处理掉
|
||||
2. "token" 返回 jwt-token 信息
|
||||
*/
|
||||
Map<String,String> map = new HashMap<>();
|
||||
map.put("error_message","success");
|
||||
map.put("token",jwt);
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package com.kob.backend.service.impl.user.account;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.kob.backend.pojo.User;
|
||||
import com.kob.backend.mapper.UserMapper;
|
||||
import com.kob.backend.service.user.account.RegisterService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class RegisterServiceImpl implements RegisterService {
|
||||
// 这里使用 @Autowired 注入是为了调用数据库,然后进行数据库查询,比较是否有用户名重复
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Override
|
||||
public Map<String, String> register(String username, String password, String confirmedPassword) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
// 如果用户名为空,则提示用户返回
|
||||
if (username == null) {
|
||||
map.put("error_message", "用户名不能为空");
|
||||
return map;
|
||||
}
|
||||
if (password == null || confirmedPassword == null) {
|
||||
map.put("error_message", "密码不能为空");
|
||||
return map;
|
||||
}
|
||||
// 用户名需要将输入的首尾空格删掉
|
||||
username = username.trim();
|
||||
if (username.length() == 0) {
|
||||
map.put("error_message", "用户名不能为空");
|
||||
return map;
|
||||
}
|
||||
if (password.length() == 0) {
|
||||
map.put("error_message", "密码不能为空");
|
||||
return map;
|
||||
}
|
||||
if (username.length() > 100) {
|
||||
map.put("error_message", "用户名过长");
|
||||
return map;
|
||||
}
|
||||
if (password.length() > 100 || confirmedPassword.length() > 100) {
|
||||
map.put("error_message", "密码过长");
|
||||
return map;
|
||||
}
|
||||
// 密码验证是 String 类型比较,应该用 equals() 方法
|
||||
if (!confirmedPassword.equals(password)) {
|
||||
map.put("error_message", "两次密码输入不一致");
|
||||
return map;
|
||||
}
|
||||
|
||||
/*
|
||||
查询数据库里是否有用户名 this.username 已存在的用户,并将结果存入 users 中,
|
||||
如果发现 users 不是空的,则告诉注册用户当前用户名已存在
|
||||
*/
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
|
||||
queryWrapper.eq("username", username);
|
||||
List<User> users = userMapper.selectList(queryWrapper);
|
||||
if (!users.isEmpty()) {
|
||||
map.put("error_message", "用户名已存在");
|
||||
return map;
|
||||
}
|
||||
|
||||
// 异常情况判断结束,开始将合法用户注册信息注入数据库
|
||||
// 对密码进行加密
|
||||
String encodedPassword = new BCryptPasswordEncoder().encode(password);
|
||||
// 默认头像
|
||||
String photo = "https://cdn.acwing.com/media/user/profile/photo/253652_lg_e3d8435b66.jpg";
|
||||
// id 是数据库自增,这里生成新用户只需要将 id 参数写为 null 即可
|
||||
User user = new User(null,username,encodedPassword,photo);
|
||||
userMapper.insert(user);
|
||||
|
||||
map.put("error_message","成功注册");
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package com.kob.backend.service.user.account;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
//根据令牌返回用户信息
|
||||
public interface InfoService {
|
||||
public Map<String,String> getInfo();
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package com.kob.backend.service.user.account;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
//验证用户名密码,验证成功后返回jwt token(令牌)
|
||||
public interface LoginService {
|
||||
public Map<String,String> getToken(String username, String password);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.kob.backend.service.user.account;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
//注册账号
|
||||
public interface RegisterService {
|
||||
// confirmedPassword 密码确认
|
||||
public Map<String,String> register(String username, String password, String confirmedPassword);
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package com.kob.backend.utils;
|
||||
|
||||
//jwt工具类,用来创建、解析 jwt token
|
||||
/*
|
||||
主要作用:
|
||||
1. 将字符串加上密钥,加上有效期,转换为加密后的字符串;
|
||||
2. 给一个令牌,将其 userID 解析出来.
|
||||
|
||||
依赖:添加到 pom.xml 中,然后使用 Maven 重新加载依赖项
|
||||
jjwt-apt;
|
||||
jjwt-impl;
|
||||
jjwt-jackson.
|
||||
*/
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
public class JwtUtil {
|
||||
public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 14; // 有效期14天:单位-毫秒 ms
|
||||
public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds121232113afasdfad"; // 密钥:随机字符串-大小写英文字母+数字
|
||||
|
||||
public static String getUUID() {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
|
||||
public static String createJWT(String subject) {
|
||||
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
|
||||
return builder.compact();
|
||||
}
|
||||
|
||||
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
|
||||
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
|
||||
SecretKey secretKey = generalKey();
|
||||
long nowMillis = System.currentTimeMillis();
|
||||
Date now = new Date(nowMillis);
|
||||
if (ttlMillis == null) {
|
||||
ttlMillis = JwtUtil.JWT_TTL;
|
||||
}
|
||||
|
||||
long expMillis = nowMillis + ttlMillis;
|
||||
Date expDate = new Date(expMillis);
|
||||
return Jwts.builder()
|
||||
.setId(uuid)
|
||||
.setSubject(subject)
|
||||
.setIssuer("sg")
|
||||
.setIssuedAt(now)
|
||||
.signWith(signatureAlgorithm, secretKey)
|
||||
.setExpiration(expDate);
|
||||
}
|
||||
|
||||
public static SecretKey generalKey() {
|
||||
byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
|
||||
return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
|
||||
}
|
||||
|
||||
public static Claims parseJWT(String jwt) throws Exception {
|
||||
SecretKey secretKey = generalKey();
|
||||
return Jwts.parserBuilder()
|
||||
.setSigningKey(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(jwt)
|
||||
.getBody();
|
||||
}
|
||||
}
|
|
@ -10,9 +10,56 @@ import NavBar from "./components/NavBar.vue";
|
|||
// 导入 BootStrap 的 css 和 js 依赖以同步 BootStrap 样式
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import "bootstrap/dist/js/bootstrap";
|
||||
// import $ from "jquery";
|
||||
|
||||
export default {
|
||||
components: { NavBar },
|
||||
/* setup: () => {
|
||||
$.ajax({
|
||||
url: "http://localhost:3000/user/account/token/",
|
||||
type: "POST",
|
||||
data: {
|
||||
username: "bb",
|
||||
password: "pbb",
|
||||
},
|
||||
success(resp) {
|
||||
console.log(resp.token, "\n成功了\n", resp.error_message);
|
||||
},
|
||||
error(resp) {
|
||||
console.log(resp);
|
||||
},
|
||||
});
|
||||
$.ajax({
|
||||
url: "http://localhost:3000/user/account/info/",
|
||||
type: "GET",
|
||||
headers: {
|
||||
Authorization:
|
||||
"Bearer " +
|
||||
"eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIzODA2Yzc0ODkwYTE0NTgwYjcyOGQzOWI4NDYyYWY2ZSIsInN1YiI6IjYiLCJpc3MiOiJzZyIsImlhdCI6MTY3Njg4ODk5MywiZXhwIjoxNjc4MDk4NTkzfQ.mq7Xdt3G7VGvX7fgIYGN2MfH8bo9MuZ7V0nfzGmbDZ8",
|
||||
},
|
||||
success(resp) {
|
||||
console.log(resp);
|
||||
},
|
||||
error(resp) {
|
||||
console.log(resp);
|
||||
},
|
||||
});
|
||||
$.ajax({
|
||||
url: "http://localhost:3000/user/account/register/",
|
||||
type: "POST",
|
||||
data: {
|
||||
username: "cc",
|
||||
password: "pcc",
|
||||
confirmedPassword: "pcc",
|
||||
},
|
||||
success(resp) {
|
||||
console.log(resp);
|
||||
},
|
||||
error(resp) {
|
||||
console.log(resp);
|
||||
},
|
||||
});
|
||||
}, */
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<!-- 判断是否登录成功:如果登录成功,则修改导航栏这部分内容 -->
|
||||
<ul class="navbar-nav" v-if="$store.state.user.is_login">
|
||||
<!-- 下拉菜单样式 -->
|
||||
<li class="nav-item dropdown">
|
||||
<a
|
||||
|
@ -37,7 +38,7 @@
|
|||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
flykhan
|
||||
{{ $store.state.user.username }}
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
|
@ -47,10 +48,36 @@
|
|||
</li>
|
||||
<!-- 下拉菜单分割线 -->
|
||||
<li><hr class="dropdown-divider" /></li>
|
||||
<li><a class="dropdown-item" href="#">退出</a></li>
|
||||
<!-- v-on:click 也可以写为 @click ; 给退出按钮加上手形状按钮样式 style="cursor: pointer"-->
|
||||
<li>
|
||||
<a class="dropdown-item" style="cursor: pointer" @click="logout">退出</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="navbar-nav" v-else>
|
||||
<!-- 下拉菜单样式 -->
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
class="nav-link"
|
||||
:to="{ name: 'user_account_login' }"
|
||||
role="button"
|
||||
>
|
||||
登录
|
||||
</router-link>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
class="nav-link"
|
||||
:to="{ name: 'user_account_register' }"
|
||||
role="button"
|
||||
>
|
||||
注册
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -59,15 +86,24 @@
|
|||
<script>
|
||||
import { useRoute } from "vue-router";
|
||||
import { computed } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
// 用于判断当前选中的是哪个 nav-link 链接,结合上文操作将选中的 nav-link 改为 nav-link active 模式
|
||||
let route_name = computed(() => route.name);
|
||||
|
||||
const logout = () => {
|
||||
console.log("退出前:" + store.state.user.token);
|
||||
store.dispatch("logout");
|
||||
console.log("退出后:" + store.state.user.token);
|
||||
};
|
||||
|
||||
return {
|
||||
route_name,
|
||||
logout,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
@ -5,6 +5,8 @@ import RanklistIndexView from '../views/ranklist/RanklistIndexView.vue'
|
|||
import RecordIndexView from '../views/record/RecordIndexView.vue'
|
||||
import UserBotIndexView from '../views/user/bot/UserBotIndexView.vue'
|
||||
import NotFound from '../views/error/NotFound.vue'
|
||||
import UserAccountLoginView from '@/views/user/account/UserAccountLoginView.vue'
|
||||
import UserAccountRegisterView from '@/views/user/account/UserAccountRegisterView.vue'
|
||||
|
||||
|
||||
// 定义所有页面的 URL 路由
|
||||
|
@ -35,6 +37,16 @@ const routes = [
|
|||
name:'user_bot_index',
|
||||
component:UserBotIndexView
|
||||
},
|
||||
{
|
||||
path:'/user/account/login/',
|
||||
name:'user_account_login',
|
||||
component:UserAccountLoginView
|
||||
},
|
||||
{
|
||||
path:'/user/account/register/',
|
||||
name:'user_account_register',
|
||||
component:UserAccountRegisterView
|
||||
},
|
||||
{
|
||||
path:'/404/',
|
||||
name:'404',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { createStore } from 'vuex'
|
||||
import ModuleUser from './user'
|
||||
|
||||
export default createStore({
|
||||
state: {
|
||||
|
@ -10,5 +11,7 @@ export default createStore({
|
|||
actions: {
|
||||
},
|
||||
modules: {
|
||||
user: ModuleUser,
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
import $ from 'jquery'
|
||||
|
||||
export default {
|
||||
state: {
|
||||
id: "",
|
||||
username: "",
|
||||
photo: "",
|
||||
token: "",
|
||||
is_login: false,
|
||||
},
|
||||
getters: {
|
||||
|
||||
},
|
||||
// 同步事件
|
||||
mutations: {
|
||||
// 更新用户信息
|
||||
updateUser(state, user){
|
||||
state.id = user.id;
|
||||
state.username = user.username;
|
||||
state.photo = user.photo;
|
||||
state.is_login = user.is_login;
|
||||
},
|
||||
// 更新用户 Token
|
||||
updateToken(state, token){
|
||||
state.token = token;
|
||||
},
|
||||
// 退出登录
|
||||
logout(state){
|
||||
state.id = "";
|
||||
state.username = "";
|
||||
state.photo = "";
|
||||
state.token = "";
|
||||
state.is_login = false;
|
||||
}
|
||||
},
|
||||
// 异步事件
|
||||
actions: {
|
||||
// 登录函数
|
||||
login(context, data){
|
||||
$.ajax({
|
||||
url: "http://localhost:3000/user/account/token/",
|
||||
type: "POST",
|
||||
data: {
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
},
|
||||
success(resp) {
|
||||
// console.log(resp.token, "\n成功了\n", resp.error_message);
|
||||
if(resp.error_message === "success"){
|
||||
/*
|
||||
登录成功则将获取到的 resp 里的 token 传给 mutations 里的
|
||||
updateToken 方法,对用户 token 信息进行更新
|
||||
*/
|
||||
context.commit("updateToken", resp.token);
|
||||
data.success(resp);
|
||||
} else {
|
||||
data.error(resp);
|
||||
}
|
||||
|
||||
},
|
||||
error(resp) {
|
||||
data.error(resp);
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获取登录成功后的用户信息
|
||||
getinfo(context,data){
|
||||
$.ajax({
|
||||
url: "http://localhost:3000/user/account/info/",
|
||||
type: "GET",
|
||||
headers: {
|
||||
Authorization:
|
||||
"Bearer " + context.state.token,
|
||||
},
|
||||
success(resp) {
|
||||
if(resp.error_message === "success"){
|
||||
// 更新用户信息
|
||||
context.commit("updateUser",{
|
||||
...resp, // 解构 resp 中的内容
|
||||
is_login: true, // 登录成功,将 is_login 置为 true
|
||||
});
|
||||
data.success(resp); // 调用回调函数
|
||||
} else {
|
||||
data.error(resp);
|
||||
}
|
||||
},
|
||||
error(resp) {
|
||||
data.error(resp);
|
||||
},
|
||||
});
|
||||
},
|
||||
logout(context){
|
||||
context.commit("logout");
|
||||
}
|
||||
|
||||
},
|
||||
modules: {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<ContentBase>
|
||||
<!-- justify-content-md-center 用于居中 -->
|
||||
<div class="row justify-content-md-center">
|
||||
<div class="col-3">
|
||||
<!-- 定义登录表单 @submita="login" 表示表单提交时触发 login 函数; submit.prevent 用于阻止 submit 的默认提交行为,
|
||||
而是调用自己定义的 login 函数来实现提交行为
|
||||
-->
|
||||
<form @submit.prevent="login">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">用户名</label>
|
||||
<!-- 使用 v-model 来绑定函数里的 username 变量 -->
|
||||
<input
|
||||
v-model="username"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="username"
|
||||
placeholder="请输入用户名"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">密码</label>
|
||||
<input
|
||||
v-model="password"
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="password"
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
</div>
|
||||
<div class="error-message">{{ error_message }}</div>
|
||||
<!-- float-start 左布局, float-end 右布局 -->
|
||||
<!-- type="submit" 提交类型 -->
|
||||
<button type="submit" class="btn btn-primary btn-sm float-center">登录</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</ContentBase>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentBase from "../../../components/ContentBase.vue";
|
||||
import { useStore } from "vuex";
|
||||
import { ref } from "vue";
|
||||
import router from "../../../router/index";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ContentBase,
|
||||
},
|
||||
setup: () => {
|
||||
const store = useStore();
|
||||
let username = ref("");
|
||||
let password = ref("");
|
||||
let error_message = ref("");
|
||||
|
||||
const login = () => {
|
||||
error_message.value = "";
|
||||
// 如果触发,则调用 store/user.js 里定义的 login 函数
|
||||
store.dispatch("login", {
|
||||
username: username.value,
|
||||
password: password.value,
|
||||
success() {
|
||||
// 登录成功,首先更新用户信息
|
||||
store.dispatch("getinfo", {
|
||||
success() {
|
||||
// 登录成功,则跳转到首页
|
||||
router.push({ name: "home" });
|
||||
console.log(store.state.user);
|
||||
},
|
||||
});
|
||||
},
|
||||
error() {
|
||||
// console.log(resp);
|
||||
error_message.value = "用户名或密码错误";
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
username,
|
||||
password,
|
||||
error_message,
|
||||
login,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div.error-message {
|
||||
color: red;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<ContentBase>注册</ContentBase>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentBase from "../../../components/ContentBase.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ContentBase,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
Loading…
Reference in New Issue