diff --git a/README.md b/README.md index f35b55f..a4d7acf 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -测试用 +## 项目说明 +本项目为安徽财经大学20届计算计科学与技术软件工程课程作业 - -测试分支功能 +内容为商品订货系统开发 @@ -22,7 +22,16 @@ 前端:Vue3 -后端:SpringBoot2 + MybitsPlus + Maven +后端:SpringBoot2 + MybaitsPlus + Maven 数据库:mysql8.0 + + + + + + +# 开发进度 + +11月26日 完成注册功能 diff --git a/backend/pom.xml b/backend/pom.xml index b3b7eb6..0152976 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -51,37 +51,37 @@ 3.5.3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + org.springframework.boot + spring-boot-starter-security + 2.7.2 + + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + + org.springframework.boot + spring-boot-starter-websocket + 2.7.3 + com.alibaba diff --git a/backend/src/main/java/com/shangping/backend/config/SecurityConfig.java b/backend/src/main/java/com/shangping/backend/config/SecurityConfig.java index e0b13bc..adb1172 100644 --- a/backend/src/main/java/com/shangping/backend/config/SecurityConfig.java +++ b/backend/src/main/java/com/shangping/backend/config/SecurityConfig.java @@ -1,5 +1,52 @@ package com.shangping.backend.config; -public class SecurityConfig { +import com.shangping.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.builders.WebSecurity; +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 extends WebSecurityConfigurerAdapter { + @Autowired + private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; + + @Bean + public PasswordEncoder passwordEncoder() { + 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/token", "/user/register").permitAll() + .antMatchers("/pk/start/game/", "/pk/receive/bot/move/").hasIpAddress("127.0.0.1") + .antMatchers(HttpMethod.OPTIONS).permitAll() + .anyRequest().authenticated(); + + http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + } + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring().antMatchers("/websocket/**"); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/shangping/backend/config/filter/JwtAuthenticationTokenFilter.java b/backend/src/main/java/com/shangping/backend/config/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..ceac62f --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/config/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,61 @@ +package com.shangping.backend.config.filter; + +import com.shangping.backend.mapper.UserMapper; +import com.shangping.backend.pojo.User; +import com.shangping.backend.service.impl.utils.UserDetailsImpl; +import com.shangping.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); + } +} diff --git a/backend/src/main/java/com/shangping/backend/controller/pk/IndexController.java b/backend/src/main/java/com/shangping/backend/controller/pk/IndexController.java index 1e470ca..da1473b 100644 --- a/backend/src/main/java/com/shangping/backend/controller/pk/IndexController.java +++ b/backend/src/main/java/com/shangping/backend/controller/pk/IndexController.java @@ -1,7 +1,5 @@ package com.shangping.backend.controller.pk; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; diff --git a/backend/src/main/java/com/shangping/backend/controller/user/LoginController.java b/backend/src/main/java/com/shangping/backend/controller/user/LoginController.java new file mode 100644 index 0000000..50e5ba7 --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/controller/user/LoginController.java @@ -0,0 +1,22 @@ +package com.shangping.backend.controller.user; + +import com.shangping.backend.service.user.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 +public class LoginController { + @Autowired + private LoginService loginService; + + @PostMapping("/user/token/") + public Map getToken(@RequestParam Map map) { + String username = map.get("username"); + String password = map.get("password"); + return loginService.getToken(username, password); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/shangping/backend/controller/user/RegisterController.java b/backend/src/main/java/com/shangping/backend/controller/user/RegisterController.java new file mode 100644 index 0000000..245a13c --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/controller/user/RegisterController.java @@ -0,0 +1,30 @@ +package com.shangping.backend.controller.user; + +import com.shangping.backend.service.user.RegisterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +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/register") + public Map register(@RequestBody Map map){ + + System.out.println("map: " + map); + + String account = map.get("account"); + String password = map.get("password"); + String confirmedPassword = map.get("confirmedPassword"); + String identity = map.get("identity"); + + return registerService.register(account, password, confirmedPassword, identity); + } +} + diff --git a/backend/src/main/java/com/shangping/backend/mapper/InvoiceMapper.java b/backend/src/main/java/com/shangping/backend/mapper/InvoiceMapper.java new file mode 100644 index 0000000..3c5ae37 --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/mapper/InvoiceMapper.java @@ -0,0 +1,4 @@ +package com.shangping.backend.mapper; + +public interface InvoiceMapper { +} diff --git a/backend/src/main/java/com/shangping/backend/mapper/OrderMapper.java b/backend/src/main/java/com/shangping/backend/mapper/OrderMapper.java new file mode 100644 index 0000000..02b992c --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/mapper/OrderMapper.java @@ -0,0 +1,4 @@ +package com.shangping.backend.mapper; + +public interface OrderMapper { +} diff --git a/backend/src/main/java/com/shangping/backend/mapper/ReimbursementMapper.java b/backend/src/main/java/com/shangping/backend/mapper/ReimbursementMapper.java new file mode 100644 index 0000000..82f35cf --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/mapper/ReimbursementMapper.java @@ -0,0 +1,4 @@ +package com.shangping.backend.mapper; + +public interface ReimbursementMapper { +} diff --git a/backend/src/main/java/com/shangping/backend/mapper/StockMapper.java b/backend/src/main/java/com/shangping/backend/mapper/StockMapper.java new file mode 100644 index 0000000..206c4b9 --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/mapper/StockMapper.java @@ -0,0 +1,4 @@ +package com.shangping.backend.mapper; + +public interface StockMapper { +} diff --git a/backend/src/main/java/com/shangping/backend/mapper/UserMapper.java b/backend/src/main/java/com/shangping/backend/mapper/UserMapper.java new file mode 100644 index 0000000..b1af9c1 --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/mapper/UserMapper.java @@ -0,0 +1,10 @@ +package com.shangping.backend.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.shangping.backend.pojo.User; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UserMapper extends BaseMapper { + +} diff --git a/backend/src/main/java/com/shangping/backend/pojo/Invoice.java b/backend/src/main/java/com/shangping/backend/pojo/Invoice.java new file mode 100644 index 0000000..300535a --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/pojo/Invoice.java @@ -0,0 +1,4 @@ +package com.shangping.backend.pojo; + +public class Invoice { +} diff --git a/backend/src/main/java/com/shangping/backend/pojo/Order.java b/backend/src/main/java/com/shangping/backend/pojo/Order.java new file mode 100644 index 0000000..b87fd6d --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/pojo/Order.java @@ -0,0 +1,4 @@ +package com.shangping.backend.pojo; + +public class Order { +} diff --git a/backend/src/main/java/com/shangping/backend/pojo/Reimbursement.java b/backend/src/main/java/com/shangping/backend/pojo/Reimbursement.java new file mode 100644 index 0000000..5c1af76 --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/pojo/Reimbursement.java @@ -0,0 +1,4 @@ +package com.shangping.backend.pojo; + +public class Reimbursement { +} diff --git a/backend/src/main/java/com/shangping/backend/pojo/Stock.java b/backend/src/main/java/com/shangping/backend/pojo/Stock.java new file mode 100644 index 0000000..27bacdc --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/pojo/Stock.java @@ -0,0 +1,4 @@ +package com.shangping.backend.pojo; + +public class Stock { +} diff --git a/backend/src/main/java/com/shangping/backend/pojo/User.java b/backend/src/main/java/com/shangping/backend/pojo/User.java new file mode 100644 index 0000000..5ff9217 --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/pojo/User.java @@ -0,0 +1,16 @@ +package com.shangping.backend.pojo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class User { + private String account; + private String nickname; + private String password; + private String identity; + private String photo; +} diff --git a/backend/src/main/java/com/shangping/backend/service/impl/user/LoginServiceImpl.java b/backend/src/main/java/com/shangping/backend/service/impl/user/LoginServiceImpl.java new file mode 100644 index 0000000..37bc4da --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/service/impl/user/LoginServiceImpl.java @@ -0,0 +1,40 @@ +package com.shangping.backend.service.impl.user; + +import com.shangping.backend.pojo.User; +import com.shangping.backend.service.impl.utils.UserDetailsImpl; +import com.shangping.backend.service.user.LoginService; +import com.shangping.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 +public class LoginServiceImpl implements LoginService { + @Autowired + private AuthenticationManager authenticationManager; + + @Override + public Map getToken(String account, String password){ + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(account, password); + + Authentication authenticate = authenticationManager.authenticate(authenticationToken); //登录失败会自动处理 + + UserDetailsImpl loginUser = (UserDetailsImpl) authenticate.getPrincipal(); + User user = loginUser.getUser(); + + String jwt = JwtUtil.createJWT(user.getAccount().toString()); + + Map map = new HashMap<>(); + map.put("error_message", "success"); + map.put("token", jwt); + + return map; + + } + +} diff --git a/backend/src/main/java/com/shangping/backend/service/impl/user/RegisterServiceImpl.java b/backend/src/main/java/com/shangping/backend/service/impl/user/RegisterServiceImpl.java new file mode 100644 index 0000000..8bc15e9 --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/service/impl/user/RegisterServiceImpl.java @@ -0,0 +1,84 @@ +package com.shangping.backend.service.impl.user; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.shangping.backend.mapper.UserMapper; +import com.shangping.backend.pojo.User; +import com.shangping.backend.service.user.RegisterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +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 register(String account, String password, String confirmedpassword, String identity) { + Map map = new HashMap<>(); + +// System.out.println("account_impl: " + account); +// System.out.println("password_impl: " + password); +// System.out.println("confirmedpassword_impl: " + confirmedpassword); +// System.out.println("identity_impl: " + identity); + + if (account == null) { + map.put("error_message", "用户名不能为空"); + return map; + } + + if (password == null || confirmedpassword == null){ + map.put("error_message", "密码不能为空"); + return map; + } + + account = account.trim(); + if (account.length() == 0){ + map.put("error_message", "用户名不能为空"); + return map; + } + + if (password.length() == 0 || confirmedpassword.length() == 0){ + map.put("error_message", "密码不能为空"); + return map; + } + + if (account.length() > 20){ + map.put("error_message", "用户名长度不能大于20"); + return map; + } + if(password.length() > 10 ||confirmedpassword.length() > 10){ + map.put("error_message", "密码长度不能大于10"); + return map; + } + + if (!password.equals(confirmedpassword)){ + map.put("error_message", "两次输入的密码不一致"); + return map; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("account", account); + List 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/81680_lg_6a90272086.jpg"; + String nickname = "用户" + account.toString(); + User user = new User(account, nickname, encodedPassword, identity, photo); + userMapper.insert(user); + map.put("code", "200"); + map.put("error_message", "success"); + return map; + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/shangping/backend/service/impl/utils/UserDetailsImpl.java b/backend/src/main/java/com/shangping/backend/service/impl/utils/UserDetailsImpl.java new file mode 100644 index 0000000..62cb744 --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/service/impl/utils/UserDetailsImpl.java @@ -0,0 +1,53 @@ +package com.shangping.backend.service.impl.utils; + +import com.shangping.backend.pojo.User; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserDetailsImpl implements UserDetails { + + private User user; + + @Override + public Collection getAuthorities() { + return null; + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getAccount(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/backend/src/main/java/com/shangping/backend/service/user/LoginService.java b/backend/src/main/java/com/shangping/backend/service/user/LoginService.java new file mode 100644 index 0000000..d043e86 --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/service/user/LoginService.java @@ -0,0 +1,7 @@ +package com.shangping.backend.service.user; + +import java.util.Map; + +public interface LoginService { + public Map getToken(String account, String password); +} diff --git a/backend/src/main/java/com/shangping/backend/service/user/RegisterService.java b/backend/src/main/java/com/shangping/backend/service/user/RegisterService.java new file mode 100644 index 0000000..bc2555d --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/service/user/RegisterService.java @@ -0,0 +1,8 @@ +package com.shangping.backend.service.user; + +import java.util.Map; + +public interface RegisterService { + + public Map register(String account, String password, String confirmedpassword, String indentity); +} diff --git a/backend/src/main/java/com/shangping/backend/utils/JwtUtil.java b/backend/src/main/java/com/shangping/backend/utils/JwtUtil.java new file mode 100644 index 0000000..8d4957e --- /dev/null +++ b/backend/src/main/java/com/shangping/backend/utils/JwtUtil.java @@ -0,0 +1,62 @@ +package com.shangping.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(); + } +} diff --git a/web/src/components/NavBar.vue b/web/src/components/NavBar.vue index 61f3d50..4f36c9b 100644 --- a/web/src/components/NavBar.vue +++ b/web/src/components/NavBar.vue @@ -38,8 +38,6 @@ export default { const router = useRouter(); const currentpage = router.currentRoute.value.name; store.commit("updatecurrentpage", currentpage); - console.log(currentpage) - console.log("store: ", store.state.currentpage) const theme1 = reactive({ color: 'rgb(141, 139, 139)' diff --git a/web/src/router/index.js b/web/src/router/index.js index 25dc0f0..813ea36 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -1,8 +1,12 @@ import { createRouter, createWebHistory } from 'vue-router' +import LoginIndexView from "../views/LoginRegister/LoginIndexView.vue" +import RegisterIndexView from "../views/LoginRegister/RegisterIndexView.vue" + import HomeIndexView from '../views/home/HomeIndexView' import KucunIndexView from "../views/kucun/KucunIndexView.vue" import DinghuoIndexView from "../views/dinghuo/DinghuoIndexView.vue" + import NotFound from '../views/error/NotFound' const routes = [ @@ -11,6 +15,16 @@ const routes = [ name: 'home', redirect:"/home/", }, + { + path:"/login", + name:"login_index", + component: LoginIndexView, + }, + { + path: "/register", + name:"register_index", + component: RegisterIndexView, + }, { path: "/home/", name: 'home_index', diff --git a/web/src/views/LoginRegister/LoginIndexView.vue b/web/src/views/LoginRegister/LoginIndexView.vue new file mode 100644 index 0000000..887551c --- /dev/null +++ b/web/src/views/LoginRegister/LoginIndexView.vue @@ -0,0 +1,255 @@ + + + + + diff --git a/web/src/views/LoginRegister/RegisterIndexView.vue b/web/src/views/LoginRegister/RegisterIndexView.vue new file mode 100644 index 0000000..89dab59 --- /dev/null +++ b/web/src/views/LoginRegister/RegisterIndexView.vue @@ -0,0 +1,256 @@ + + + + +