version-1.8[注册+登录+用户信息(后端APUI+前端Vue)(localStore+ajax)]

pull/1/head
dyh 3 years ago
parent c2051b9595
commit eff1294ca6

@ -16,6 +16,32 @@
</properties>
<dependencies>
<!-- 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>
<!-- 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-api -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>

@ -13,3 +13,4 @@ public class BackendApplication {
}
}

@ -1,20 +1,48 @@
//实现用户密码的加密存储。==>加上此文件,必须是密文存储
//实现公开页面
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;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();//返回加密方法
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@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,61 @@
//用来验证jwt token如果验证成功则将User信息注入上下文中
package com.kob.backend.config.filter;
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 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");
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);
}
}

@ -1,79 +1,79 @@
package com.kob.backend.controller.user;
//@ResponseBody 把return的结果变为Json等数据
//@Controller 返回jsp,html页面+跳转
//@Controller + @ResponseBody 返回json等数据
//@RestController 返回json等数据,不能是jsp,html【@Controller+@responseBody】
//@RequestMapping 给出外界访问方法的路径,或者说触发路径 ,触发条件
//@GetMapping
//PostMapping
//@AutoWired自动导入依赖的bean。[用到mapper层的接口时,数据库,写上去]
//byType方式。把配置好的Bean拿来用完成属性、方法的组装它可以对类成员变量、方法及构造函数进行标注完成自动装配的工作。
//当加上required=false就算找不到bean也不报错。
//它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
//mapper接口 被mybatis实现
//接口实例调用mybatis的crud
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//@@@@@@@@@@@@@@@ 实现了【增删查】 @@@@@@@@@@@@@@@@
@RestController
public class UserController {
@Autowired
UserMapper userMapper;
/*查询所有用户*/
@GetMapping("/user/all/")
public List<User> getAll()
{
return userMapper.selectList(null);
}
/*查询单个用户*/
@GetMapping("/user/{userId}/")
public User getUser(@PathVariable int userId)
{
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id",userId);
return userMapper.selectOne(queryWrapper);
// 范围遍历
// public List<User> getUser(int userId)
// queryWrapper.ge("id", 2).le("id", 3);
// return userMapper.selectList(queryWrapper);
}
/*添加一个用户*/
@GetMapping("/user/add/{userId}/{username}/{password}/")
public String addUser(@PathVariable int userId,
@PathVariable String username,
@PathVariable String password)
{
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encoderPassword = passwordEncoder.encode(password);
User user = new User(userId,username,encoderPassword);
userMapper.insert(user);
return "Add User Sucessfully";
}
/*删除一个用户*/
@GetMapping("/user/delete/{userId}/")
public String deleteUser(@PathVariable int userId)
{
userMapper.deleteById(userId);
return "Delete User Successfully";
}
}
//package com.kob.backend.controller.user;
//
//
////@ResponseBody 把return的结果变为Json等数据
////@Controller 返回jsp,html页面+跳转
////@Controller + @ResponseBody 返回json等数据
////@RestController 返回json等数据,不能是jsp,html【@Controller+@responseBody】
//
////@RequestMapping 给出外界访问方法的路径,或者说触发路径 ,触发条件
////@GetMapping
////PostMapping
//
////@AutoWired自动导入依赖的bean。[用到mapper层的接口时,数据库,写上去]
////byType方式。把配置好的Bean拿来用完成属性、方法的组装它可以对类成员变量、方法及构造函数进行标注完成自动装配的工作。
////当加上required=false就算找不到bean也不报错。
////它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
//
////mapper接口 被mybatis实现
////接口实例调用mybatis的crud
//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
//import com.kob.backend.mapper.UserMapper;
//import com.kob.backend.pojo.User;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
//import org.springframework.security.crypto.password.PasswordEncoder;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.PathVariable;
//import org.springframework.web.bind.annotation.RestController;
//
//import java.util.List;
////@@@@@@@@@@@@@@@ 实现了【增删查】 @@@@@@@@@@@@@@@@
//@RestController
//public class UserController {
//
// @Autowired
// UserMapper userMapper;
//
// /*查询所有用户*/
// @GetMapping("/user/all/")
// public List<User> getAll()
// {
// return userMapper.selectList(null);
// }
//
// /*查询单个用户*/
// @GetMapping("/user/{userId}/")
// public User getUser(@PathVariable int userId)
// {
// QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("id",userId);
// return userMapper.selectOne(queryWrapper);
// // 范围遍历
// // public List<User> getUser(int userId)
// // queryWrapper.ge("id", 2).le("id", 3);
// // return userMapper.selectList(queryWrapper);
// }
//
// /*添加一个用户*/
// @GetMapping("/user/add/{userId}/{username}/{password}/")
// public String addUser(@PathVariable int userId,
// @PathVariable String username,
// @PathVariable String password)
// {
// PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// String encoderPassword = passwordEncoder.encode(password);
// User user = new User(userId,username,encoderPassword,"00");
// userMapper.insert(user);
// return "Add User Sucessfully";
// }
//
// /*删除一个用户*/
// @GetMapping("/user/delete/{userId}/")
// public String deleteUser(@PathVariable int userId)
// {
// userMapper.deleteById(userId);
// return "Delete 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,31 @@
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.GetMapping;
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;
//@GetMapping 直接从URL获得数据以www.xxx.com/?id=1类似这种形式传输
//@PostMapping 请求会从body部分拿到数据好处是一些不希望用户看到的数据会放在body里面传输。
//返回数据
@RestController
public class LoginController {
@Autowired
private LoginService loginService;//接口实例调用接口方法==>调用实现类中的方法
@PostMapping("/user/account/token/")
public Map<String,String> getToken(@RequestParam Map<String,String> map)
{
String username = map.get("username");
String password = map.get("password");
// System.out.println("------------");
return loginService.getToken(username,password);
}
}

@ -0,0 +1,26 @@
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);
}
}

@ -1,5 +1,7 @@
package com.kob.backend.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -15,7 +17,10 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@Data
public class User {
@TableId(type = IdType.AUTO)
private Integer id;
private String username;
private String password;
private String photo;
}

@ -1,5 +1,5 @@
package com.kob.backend.service.impl;
//关联数据库 【加载用户通过用户名 返回UserDetails接口实例】====》明文登录
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.service.impl.utils.UserDetailsImpl;

@ -0,0 +1,33 @@
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();
//取出用户(实例化UserDetailsImp类)
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,42 @@
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;
//AuthenticationManager接口 是认证相关的核心接口,也是发起认证的出发点
//
import java.util.HashMap;
import java.util.Map;
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public Map<String, String> getToken(String username, String password) {
//将用户名+密码加密封装
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username,password);
//身份认证方法==>登录失败,自动处理
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
//登录成功==>取出用户(实例化UserDetailsImp类)
UserDetailsImpl loginUser = (UserDetailsImpl) authenticate.getPrincipal();
User user = loginUser.getUser();//因为UserDetailsImpl有@Data
//取出Token==>通过主键生成jwt_token
String jwt = JwtUtil.createJWT(user.getId().toString());
Map<String,String> map = new HashMap<>();
map.put("error_message","success");
map.put("token",jwt);
return map;
}
}

@ -0,0 +1,88 @@
package com.kob.backend.service.impl.user.account;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.User;
import com.kob.backend.service.user.account.RegisterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class RegisterServiceImpl implements RegisterService {
@Autowired
private UserMapper userMapper;
@Autowired
private PasswordEncoder passwordEncoder;
@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 || confirmedPassword.length() == 0)//密码可以有空格
{
map.put("error_message","密码不能为空");
return map;
}
if(username.length() > 100)
{
map.put("error_message","用户名长度不能大于100");
return map;
}
if (password.length() > 100 || confirmedPassword.length() > 100) {
map.put("error_message", "密码不能大于100");
return map;
}
if(password.equals(confirmedPassword) == false)
{
map.put("error_message","两次密码不相同");
return map;
}
//查询用户名是否已经存在
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",username);
List<User> users = userMapper.selectList(queryWrapper);
if(!users.isEmpty()){
map.put("error_message","用户名已存在");
return map;
}
//添加一个用户
String encodedPassword = passwordEncoder.encode(password);//密码
String photo = "https://cdn.acwing.com/media/user/profile/photo/85918_lg_5ed2550a14.png";
User user = new User(null,username,encodedPassword,photo);
userMapper.insert(user);
map.put("error_message","success");
return map;
}
}

@ -1,5 +1,5 @@
package com.kob.backend.service.impl.utils;
//个人信息接口的实现====》明文登录
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -30,6 +30,7 @@ public class UserDetailsImpl implements UserDetails
@Override
public String getUsername() { return user.getUsername();}
//isAccountNonExpired 账户没有过期
@Override
public boolean isAccountNonExpired() {//账号没过期

@ -0,0 +1,11 @@
package com.kob.backend.service.user.account;
//根据令牌返回用户信息
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public interface InfoService {
public Map<String,String> getInfo();
}

@ -0,0 +1,11 @@
package com.kob.backend.service.user.account;
import org.springframework.stereotype.Service;
import java.util.Map;
//验证用户名密码,验证成功后返回 jwt token令牌
@Service
public interface LoginService {
public Map<String,String> getToken(String username,String password);
}

@ -0,0 +1,10 @@
package com.kob.backend.service.user.account;
//注册账号
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public interface RegisterService {
public Map<String, String> register(String username, String password, String confirmedPassword);
}

@ -0,0 +1,63 @@
//JwtUtil 类为jwt 工具类 用来创建、解析 jwt token
package com.kob.backend.utils;
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天
public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds121232131afasdfac";//密钥
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();
}
}

@ -6,33 +6,86 @@
<script>
// import $ from "jquery"
// import { ref } from "vue"
import NavBar from './components/NavBar.vue';
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.min.js";
// import "bootstrap/dist/js/bootstrap";
export default {
components: { NavBar },
name:"app",
// setup:()=>{
// let bot_name = ref("");
// let bot_rating = ref("");
setup:()=>{
// // LoginService==>token
// $.ajax({
// url:"http://localhost:3000/pk/getbotinfo/",
// url:"http://localhost:3000/user/account/token/",
// type:"post",
// data:{
// username:'yxc',
// password:'yxc',
// },
// success(resp){
// console.log(resp);
// },
// errror(resp){
// console.log(resp);
// }
// });
// // InfoService==>info
// $.ajax({
// url:"http://localhost:3000/user/account/info/",
// type:"get",
// success:resp=>{
// headers:{
// Authorization:"Bearer "+"eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0MmEwMmM3NGIwM2I0ODIzOGJmMzliMGIwOWNiMzYxOSIsInN1YiI6IjEiLCJpc3MiOiJzZyIsImlhdCI6MTY2MDAxNTA1NiwiZXhwIjoxNjYxMjI0NjU2fQ.1KXxdlCQQ1i-xMjKoiocqFvWDgY24L9rLzo233z2Tfk",
// },
// success(resp)
// {
// console.log(resp);
// },
// errror(resp)
// {
// console.log(resp);
// }
// });
// // RegisterService==>register
// $.ajax({
// url:"http://localhost:3000/user/account/register/",
// type:"post",
// data:{
// username: "yxc3",
// password:"yxc3",
// confirmedPassword: "yxc3",
// },
// success(resp){
// console.log(resp);
// },
// errror(resp){
// console.log(resp);
// bot_name.value = resp.name;
// bot_rating.value = resp.rating;
// }
// });
// return {
// bot_name,
// bot_rating
// }
// }
// InfoService==>info
// $.ajax({
// url:"https://app165.acapp.acwing.com.cn/myspace/userlist/",
// type:"get",
// // headers:{
// // Authorization:"Bearer "+"eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0MmEwMmM3NGIwM2I0ODIzOGJmMzliMGIwOWNiMzYxOSIsInN1YiI6IjEiLCJpc3MiOiJzZyIsImlhdCI6MTY2MDAxNTA1NiwiZXhwIjoxNjYxMjI0NjU2fQ.1KXxdlCQQ1i-xMjKoiocqFvWDgY24L9rLzo233z2Tfk",
// // },
// success(resp)
// {
// console.log(resp);
// },
// errror(resp)
// {
// console.log(resp);
// }
// });
}
}
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

@ -41,7 +41,6 @@ export class AcGameObject
//上一秒执行的时刻
let last_timestamp;
@ -65,6 +64,7 @@ const step = timestamp => {
//【迭代】浏览器每一秒会迭代60次
requestAnimationFrame(step);
}
//浏览器刷新 -> 执行一次

@ -18,7 +18,7 @@ export class GameMap extends AcGameObject
this.walls=[];
this.inner_walls_count = 0;
this.inner_walls_count = 5;
this.snakes = [
new Snake({id:0,color:"#4876ED",r:this.rows-2,c:1},this),

@ -25,24 +25,31 @@
</li>
</ul>
<ul class="navbar-nav">
<ul class="navbar-nav" v-if="$store.state.user.is_login">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
杜艳贺
{{$store.state.user.username}}
</a>
<!-- --------注意----------------- -->
<!-- 下拉框的警告解决style="margin:0" -->
<!-- -------------------------------- -->
<ul class="dropdown-menu" aria-labelledby="navbarDropdown" style="margin:0">
<router-link class="dropdown-item" :to="{name:'user_bot_index'}">我的Bots</router-link>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">退出</a></li>
<li><a class="dropdown-item" @click="logout" href="#">退出</a></li>
</ul>
</li>
</ul>
<ul class="navbar-nav" v-else-if="!$store.state.user.pulling_info">
<li class="nav-item">
<router-link class="nav-link" :to="{name:'user_account_login'}" role="button" aria-expanded="false">
登录
</router-link>
</li>
<li class="nav-item">
<router-link class="nav-link" :to="{name:'user_account_register'}" href="#" role="button" aria-expanded="false">
注册
</router-link>
</li>
</ul>
</div>
</div>
@ -53,16 +60,24 @@
// {}
import { useRoute } from "vue-router";
import { computed } from "vue";
import { useStore } from "vuex";
export default {
//
setup(){
const route = useRoute();
const store = useStore();
//
let route_name = computed( () => route.name );
const logout = () => {
store.dispatch("logout");
}
return {
route_name
route_name,
logout,
}
}

@ -4,42 +4,83 @@ import PkIndexView from '../views/pk/PkIndexView'
import RanklistIndexView from '../views/ranklist/RanklistIndexView'
import RecordIndexView from '../views/record/RecordIndexView'
import UserBotIndexView from '../views/user/bot/UserBotIndexView'
import UserAccountLoginView from '@/views/user/account/UserAccountLoginView'
import UserAccountRegisterView from '@/views/user/account/UserAccountRegisterView'
import store from "@/store/index"
//setup里才有useStore
const routes = [
{
{
path: "/",
name:"home",
redirect:"/pk/",
meta:{
requestAuth : true ,
}
},
//pk
{
path:"/pk/",
name:"pk_index",
component:PkIndexView,
meta:{
requestAuth : true ,
}
},
//对战列表
{
path:"/record/",
name:"record_index",
component:RecordIndexView,
meta:{
requestAuth : true ,
}
},
//排行榜
{
path:"/ranklist/",
name:"ranklist_index",
component:RanklistIndexView,
meta:{
requestAuth : true ,
}
},
//我的Bot
{
path:"/user/bot/",
name:"user_bot_index",
component:UserBotIndexView,
meta:{
requestAuth : true ,
}
},
//登录
{
path:"/user/account/login/",
name:"user_account_login",
component:UserAccountLoginView,
meta:{
requestAuth : false ,
}
},
//注册
{
path:"/user/account/register/",
name:"user_account_register",
component:UserAccountRegisterView,
meta:{
requestAuth : false ,
}
},
//404页面
{
path:"/404/",
name:"404",
component:NotFound,
meta:{
requestAuth : false ,
}
},
//匹配其他未路由的页面
{
@ -47,7 +88,6 @@ const routes = [
redirect:"/404/",
}
]
//Hash有#
@ -56,4 +96,15 @@ const router = createRouter({
routes
})
//未登录时:点击授权页面会跳转到
router.beforeEach((to,from,next)=>{
if(to.meta.requestAuth && !store.state.user.is_login ){
next({name:"user_account_login"});
} else {
next();
}
})
export default router

@ -1,4 +1,6 @@
//主页面
import { createStore } from 'vuex'
import ModuleUser from './user';
export default createStore({
state: {
@ -10,5 +12,6 @@ export default createStore({
actions: {
},
modules: {
user:ModuleUser,
}
})

@ -0,0 +1,105 @@
import $ from "jquery"
//全局共享量
export default {
state: {
id:'',
username:'',
password:'',
photo:'',
token:'',
is_login:false,
pulling_info:true,//是否正在云端获取信息
},
getters: {
},
mutations: {
updateUser(state,user)//state value
{
state.id = user.id;
state.username = user.username;
state.photo = user.photo;
state.is_login = user.is_login;
},
updateToken(state,token)
{
state.token = token;
},
logout(state)
{
state.id = "";
state.username = "";
state.password = "";
state.photo = "";
state.token = "";
state.is_login = false;
},
updatePullingInfo(state,pulling_info){
state.pulling_info = pulling_info;
}
},
actions: {
//更新token
login(context, data) {//context value
$.ajax({
url: "http://127.0.0.1:3000/user/account/token/",
type: "post",
data: {
username: data.username,
password: data.password,
},
success(resp) {
if (resp.error_message === "success") {
localStorage.setItem("jwt_token", resp.token);
context.commit("updateToken", resp.token);//调用mutations
data.success(resp);
} else {
data.error(resp);
}
},
error(resp) {
data.error(resp);
}
});
},
//更新user信息
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,//解构id,username,password,error_message
is_login: true,
});
data.success(resp);
} else {
data.error(resp);
}
},
error(resp){
data.error(resp);
}
});
},
logout(context)
{
localStorage.removeItem("jwt_token")
context.commit("logout")
}
},
modules: {
}
}

@ -1,6 +1,6 @@
<template>
<ContentField>
404
404 Not Found
</ContentField>
</template>

@ -0,0 +1,104 @@
<template>
<ContentField v-if="!$store.state.user.pulling_info">
<div class="row justify-content-md-center">
<div class="col-3">
<form @submit.prevent="login">
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<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>
<button type="submit" class="btn btn-primary center">登录</button>
</form>
</div>
</div>
</ContentField>
</template>
<script>
import ContentField from '../../../components/ContentField.vue'
import { useStore } from 'vuex'
import { ref } from 'vue'
import router from "@/router/index"
export default {
components: {
ContentField
},
setup() {
const store = useStore();
let username = ref('');
let password = ref('');
let error_message = ref('');
const jwt_token = localStorage.getItem('jwt_token');
//token
//token
if(jwt_token)//token
{
store.commit("updateToken",jwt_token)//mutations
store.dispatch("getinfo",{//action
success(){
console.log(store.state.user.pulling_info)
router.push({name:"home"})
store.commit("updatePullingInfo",false)//
console.log(store.state.user.pulling_info)
},
error(){
store.commit("updatePullingInfo",false)
}
})
}
else//token
{
store.commit("updatePullingInfo",false)
}
//
const login = () => {
error_message.value = "";
//login
store.dispatch("login", {
username: username.value,
password: password.value,
success() {
//getinfo
store.dispatch("getinfo",{
success(){
router.push({name:"home"});
// console.log(store.state.user);
}
})
},
error() {
error_message.value = "用户名or密码错误";
}
})
}
return {
username,
password,
error_message,
login,
}
}
}
</script>
<style scoped>
button {
width: 100%;
}
div.error-message {
color: red;
}
</style>

@ -0,0 +1,83 @@
<template>
<ContentField>
<div class="row justify-content-md-center">
<div class="col-3">
<form @submit.prevent="login">
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<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="mb-3">
<label for="confirmedPassword" class="form-label">重复密码</label>
<input v-model="confirmedPassword" type="password" class="form-control" id="confirmedPassword" placeholder="请重复输入密码">
</div>
<div class="error-message"> {{error_message}} </div>
<button @click="register" type="submit" class="btn btn-primary center">注册</button>
</form>
</div>
</div>
</ContentField>
</template>
<script>
import ContentField from "@/components/ContentField";
// import { useStore } from "vuex"
import { ref } from "vue"
import router from "@/router/index"
import $ from "jquery"
export default {
components: { ContentField },
setup(){
// const store = useStore();
let username = ref('')
let password = ref('')
let confirmedPassword = ref('')
let error_message = ref('')
const register = () =>{
$.ajax({
url:"http://localhost:3000/user/account/register/",
type:"post",
data:{
//500
username: username.value,
password: password.value,
confirmedPassword: confirmedPassword.value,
},
success(resp){
if(resp.error_message === "success"){
router.push({name:"user_account_login"})
} else {
error_message.value = resp.error_message ;
}
}
})
}
return {
username,
password,
confirmedPassword,
error_message,
register,
}
}
}
</script>
<style scoped>
button {
width: 100%;
}
div.error-message {
color: red;
}
</style>

@ -0,0 +1,70 @@
<template>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<router-link class='navbar-brand' :to="{name:'home'}">King Of Bots</router-link>
<!-- <a class="navbar-brand" href="#">King Of Bots</a> -->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<!-- <a class="nav-link active" aria-current="page" href="/pk/">对战</a> -->
<!-- router-link单页面 -->
<router-link :class="route_name == 'pk_index' ? 'nav-link active' : 'nav-link' " :to="{name:'pk_index'}">PK</router-link>
</li>
<li class="nav-item">
<!-- <a class="nav-link" href="/record/">对局列表</a> -->
<router-link :class="route_name == 'record_index' ? 'nav-link active' : 'nav-link' " :to="{name:'record_index'}">对局列表</router-link>
</li>
<li class="nav-item">
<!-- <a class="nav-link" href="/ranklist/">排行榜</a> -->
<router-link :class="route_name == 'ranklist_index' ? 'nav-link active' : 'nav-link' " :to="{name:'ranklist_index'}">排行榜</router-link>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
{{this.$store.state.user.is_login}}
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown" style="margin:0">
<router-link class="dropdown-item" :to="{name:'user_bot_index'}">我的Bots</router-link>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">退出</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
</template>
<script>
// {}
import { useRoute } from "vue-router";
import { computed } from "vue";
export default {
//
setup(){
const route = useRoute();
//
let route_name = computed( () => route.name );
return {
route_name
}
}
}
</script>
<style scoped>
</style>
Loading…
Cancel
Save