@ -0,0 +1,33 @@ | |||||
HELP.md | |||||
target/ | |||||
!.mvn/wrapper/maven-wrapper.jar | |||||
!**/src/main/**/target/ | |||||
!**/src/test/**/target/ | |||||
### STS ### | |||||
.apt_generated | |||||
.classpath | |||||
.factorypath | |||||
.project | |||||
.settings | |||||
.springBeans | |||||
.sts4-cache | |||||
### IntelliJ IDEA ### | |||||
.idea | |||||
*.iws | |||||
*.iml | |||||
*.ipr | |||||
### NetBeans ### | |||||
/nbproject/private/ | |||||
/nbbuild/ | |||||
/dist/ | |||||
/nbdist/ | |||||
/.nb-gradle/ | |||||
build/ | |||||
!**/src/main/**/build/ | |||||
!**/src/test/**/build/ | |||||
### VS Code ### | |||||
.vscode/ |
@ -0,0 +1,115 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<groupId>cn.edu.ecnu.stu</groupId> | |||||
<artifactId>bookstore</artifactId> | |||||
<version>0.0.1-SNAPSHOT</version> | |||||
<name>bookstore</name> | |||||
<description>bookstore</description> | |||||
<properties> | |||||
<java.version>1.8</java.version> | |||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | |||||
<spring-boot.version>2.6.13</spring-boot.version> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-web</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.mybatis.spring.boot</groupId> | |||||
<artifactId>mybatis-spring-boot-starter</artifactId> | |||||
<version>2.2.2</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.mysql</groupId> | |||||
<artifactId>mysql-connector-j</artifactId> | |||||
<scope>runtime</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.projectlombok</groupId> | |||||
<artifactId>lombok</artifactId> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-test</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-security</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.alibaba</groupId> | |||||
<artifactId>druid</artifactId> | |||||
<version>1.1.9</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.auth0</groupId> | |||||
<artifactId>java-jwt</artifactId> | |||||
<version>3.4.0</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.alibaba</groupId> | |||||
<artifactId>fastjson</artifactId> | |||||
<version>1.2.47</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-data-redis</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.apache.httpcomponents</groupId> | |||||
<artifactId>httpclient</artifactId> | |||||
<version>4.5.13</version> | |||||
</dependency> | |||||
</dependencies> | |||||
<dependencyManagement> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-dependencies</artifactId> | |||||
<version>${spring-boot.version}</version> | |||||
<type>pom</type> | |||||
<scope>import</scope> | |||||
</dependency> | |||||
</dependencies> | |||||
</dependencyManagement> | |||||
<build> | |||||
<plugins> | |||||
<plugin> | |||||
<groupId>org.apache.maven.plugins</groupId> | |||||
<artifactId>maven-compiler-plugin</artifactId> | |||||
<version>3.8.1</version> | |||||
<configuration> | |||||
<source>1.8</source> | |||||
<target>1.8</target> | |||||
<encoding>UTF-8</encoding> | |||||
</configuration> | |||||
</plugin> | |||||
<plugin> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-maven-plugin</artifactId> | |||||
<version>${spring-boot.version}</version> | |||||
<configuration> | |||||
<mainClass>cn.edu.ecnu.stu.bookstore.BookstoreApplication</mainClass> | |||||
<skip>true</skip> | |||||
</configuration> | |||||
<executions> | |||||
<execution> | |||||
<id>repackage</id> | |||||
<goals> | |||||
<goal>repackage</goal> | |||||
</goals> | |||||
</execution> | |||||
</executions> | |||||
</plugin> | |||||
</plugins> | |||||
</build> | |||||
</project> |
@ -0,0 +1,13 @@ | |||||
package cn.edu.ecnu.stu.bookstore; | |||||
import org.springframework.boot.SpringApplication; | |||||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||||
@SpringBootApplication | |||||
public class BookstoreApplication { | |||||
public static void main(String[] args) { | |||||
SpringApplication.run(BookstoreApplication.class, args); | |||||
} | |||||
} |
@ -0,0 +1,28 @@ | |||||
package cn.edu.ecnu.stu.bookstore.component; | |||||
import lombok.AllArgsConstructor; | |||||
@AllArgsConstructor | |||||
public class AppException extends RuntimeException{ | |||||
private String code; | |||||
private String message; | |||||
public void setCode(String code) { | |||||
this.code = code; | |||||
} | |||||
public void setMessage(String message) { | |||||
this.message = message; | |||||
} | |||||
public String getCode() { | |||||
return code; | |||||
} | |||||
@Override | |||||
public String getMessage() { | |||||
return message; | |||||
} | |||||
} |
@ -0,0 +1,18 @@ | |||||
package cn.edu.ecnu.stu.bookstore.component; | |||||
public interface Constants { | |||||
String SYSTEM_ERROR = "500"; | |||||
String CLIENT_ERROR = "400"; | |||||
String SUCCESS = "200"; | |||||
String AUTHENTICATION_ERROR = "401"; | |||||
String UNAUTHORIZED_ERROR = "403"; | |||||
String REGISTER_ERROR_MESSAGE = "用户名已存在"; | |||||
String PARAMETER_ERROR_MESSAGE = "参数非法"; | |||||
String SUCCESS_MESSAGE = "ok"; | |||||
String AUTHENTICATION_ERROR_MESSAGE = "用户未认证或token过期,请先登录"; | |||||
String PASSWORD_ERROR = "用户名不存在或密码错误"; | |||||
String URL_PREFIX = "http://127.0.0.1:8080"; | |||||
} |
@ -0,0 +1,46 @@ | |||||
package cn.edu.ecnu.stu.bookstore.component; | |||||
import lombok.Data; | |||||
@Data | |||||
public class Result { | |||||
private Object data; | |||||
private String message; | |||||
private String code; | |||||
public Result(String code, String message, Object data) { | |||||
this.code = code; | |||||
this.message = message; | |||||
this.data = data; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "Result{" + | |||||
"data=" + data + | |||||
", message='" + message + '\'' + | |||||
", code='" + code + '\'' + | |||||
'}'; | |||||
} | |||||
public static Result success(Object data) { | |||||
return new Result(Constants.SUCCESS, "ok", data); | |||||
} | |||||
public static Result error(String code, String message) { | |||||
return new Result(code, message, null); | |||||
} | |||||
public static Result error(String code, String message, Object data) { | |||||
return new Result(code, message, data); | |||||
} | |||||
public static Result success() { | |||||
return new Result(Constants.SUCCESS, Constants.SUCCESS_MESSAGE, null); | |||||
} | |||||
} |
@ -0,0 +1,31 @@ | |||||
package cn.edu.ecnu.stu.bookstore.config; | |||||
import org.springframework.beans.factory.InitializingBean; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import org.springframework.data.redis.core.RedisTemplate; | |||||
import org.springframework.data.redis.serializer.RedisSerializer; | |||||
import org.springframework.data.redis.serializer.StringRedisSerializer; | |||||
@Configuration | |||||
public class RedisConfig implements InitializingBean { | |||||
@Autowired | |||||
private RedisTemplate<Object, Object> redisTemplate; | |||||
@Override | |||||
public void afterPropertiesSet() throws Exception { | |||||
RedisSerializer stringSerializer = new StringRedisSerializer(); | |||||
//key序列化方式 | |||||
redisTemplate.setKeySerializer(stringSerializer); | |||||
//String的序列化方式 | |||||
redisTemplate.setStringSerializer(stringSerializer); | |||||
//value序列化方式 | |||||
redisTemplate.setValueSerializer(stringSerializer); | |||||
//hash key序列化方式 | |||||
redisTemplate.setHashKeySerializer(stringSerializer); | |||||
//hash value序列化方式 | |||||
redisTemplate.setHashValueSerializer(stringSerializer); | |||||
} | |||||
} |
@ -0,0 +1,51 @@ | |||||
package cn.edu.ecnu.stu.bookstore.config; | |||||
import cn.edu.ecnu.stu.bookstore.filter.AuthenticationFilter; | |||||
import cn.edu.ecnu.stu.bookstore.handler.AuthenticationEntryPointImpl; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import org.springframework.security.authentication.AuthenticationManager; | |||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |||||
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.AuthenticationEntryPoint; | |||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | |||||
@Configuration | |||||
public class SecurityConfig extends WebSecurityConfigurerAdapter { | |||||
@Bean | |||||
public PasswordEncoder passwordEncoder() { | |||||
return new BCryptPasswordEncoder(); | |||||
} | |||||
@Autowired | |||||
public AuthenticationFilter authenticationFilter; | |||||
@Bean | |||||
public AuthenticationEntryPoint authenticationEntryPoint() { | |||||
return new AuthenticationEntryPointImpl(); | |||||
} | |||||
@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("/auth/*").permitAll() | |||||
.anyRequest().authenticated(); | |||||
http.addFilterBefore(authenticationFilter, UsernamePasswordAuthenticationFilter.class); | |||||
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()); | |||||
} | |||||
} |
@ -0,0 +1,54 @@ | |||||
package cn.edu.ecnu.stu.bookstore.controller; | |||||
import cn.edu.ecnu.stu.bookstore.component.Result; | |||||
import cn.edu.ecnu.stu.bookstore.pojo.User; | |||||
import cn.edu.ecnu.stu.bookstore.service.UserService; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.web.bind.annotation.*; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
@RestController | |||||
@RequestMapping("/auth") | |||||
public class AuthController { | |||||
@Autowired | |||||
private UserService userService; | |||||
@PostMapping("/register") | |||||
public Result register(@RequestBody User user) { | |||||
userService.register(user); | |||||
return Result.success(); | |||||
} | |||||
@PostMapping("/unregister") | |||||
public Result unregister(@RequestBody User user) { | |||||
userService.unregister(user); | |||||
return Result.success(); | |||||
} | |||||
@PostMapping("/login") | |||||
public Result login(@RequestBody User user) { | |||||
String token = userService.login(user); | |||||
HashMap<String, Object> map = new HashMap<>(); | |||||
map.put("token", token); | |||||
return Result.success(map); | |||||
} | |||||
@PostMapping("/password") | |||||
public Result changePassword(@RequestBody Map<String, String> map) { | |||||
String username = map.get("username"); | |||||
String oldPassword = map.get("oldPassword"); | |||||
String newPassword = map.get("newPassword"); | |||||
userService.changePassword(username, oldPassword, newPassword); | |||||
return Result.success(); | |||||
} | |||||
@PostMapping("/logout") | |||||
public Result logout(@RequestBody Map<String, String> map) { | |||||
userService.logout(map.get("username")); | |||||
return Result.success(); | |||||
} | |||||
} | |||||
@ -0,0 +1,15 @@ | |||||
package cn.edu.ecnu.stu.bookstore.controller; | |||||
import cn.edu.ecnu.stu.bookstore.component.Result; | |||||
import org.springframework.stereotype.Controller; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
@RestController | |||||
public class TestController { | |||||
@GetMapping("/test") | |||||
public Result test() { | |||||
return Result.success("hello, test"); | |||||
} | |||||
} |
@ -0,0 +1,46 @@ | |||||
package cn.edu.ecnu.stu.bookstore.filter; | |||||
import cn.edu.ecnu.stu.bookstore.pojo.User; | |||||
import cn.edu.ecnu.stu.bookstore.utils.JwtUtil; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.data.redis.core.RedisTemplate; | |||||
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 AuthenticationFilter extends OncePerRequestFilter { | |||||
@Autowired | |||||
private RedisTemplate redisTemplate; | |||||
private boolean hasLogin(Integer userId) { | |||||
String s = (String)redisTemplate.opsForValue().get("userId:" + userId); | |||||
return StringUtils.hasText(s); | |||||
} | |||||
@Override | |||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { | |||||
String token = request.getHeader("token"); | |||||
if(!StringUtils.hasText(token)){ | |||||
filterChain.doFilter(request, response); | |||||
return; | |||||
} | |||||
User user = (User) JwtUtil.getTokenInfo(token, User.class); | |||||
if(!hasLogin(user.getId())) { | |||||
filterChain.doFilter(request, response); | |||||
return; | |||||
} | |||||
UsernamePasswordAuthenticationToken token1 = new UsernamePasswordAuthenticationToken(user, null, null); | |||||
SecurityContextHolder.getContext().setAuthentication(token1); | |||||
filterChain.doFilter(request, response); | |||||
} | |||||
} |
@ -0,0 +1,22 @@ | |||||
package cn.edu.ecnu.stu.bookstore.handler; | |||||
import cn.edu.ecnu.stu.bookstore.component.Constants; | |||||
import cn.edu.ecnu.stu.bookstore.component.Result; | |||||
import com.alibaba.fastjson.JSON; | |||||
import org.springframework.security.core.AuthenticationException; | |||||
import org.springframework.security.web.AuthenticationEntryPoint; | |||||
import javax.servlet.ServletException; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
import java.io.IOException; | |||||
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint { | |||||
@Override | |||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { | |||||
response.setStatus(200); | |||||
response.setContentType("application/json"); | |||||
response.setCharacterEncoding("utf-8"); | |||||
response.getWriter().print(JSON.toJSONString(Result.error(Constants.AUTHENTICATION_ERROR, Constants.AUTHENTICATION_ERROR_MESSAGE))); | |||||
} | |||||
} |
@ -0,0 +1,24 @@ | |||||
package cn.edu.ecnu.stu.bookstore.handler; | |||||
import cn.edu.ecnu.stu.bookstore.component.AppException; | |||||
import cn.edu.ecnu.stu.bookstore.component.Constants; | |||||
import cn.edu.ecnu.stu.bookstore.component.Result; | |||||
import org.springframework.web.bind.annotation.ControllerAdvice; | |||||
import org.springframework.web.bind.annotation.ExceptionHandler; | |||||
import org.springframework.web.bind.annotation.ResponseBody; | |||||
import org.springframework.web.bind.annotation.RestControllerAdvice; | |||||
@RestControllerAdvice | |||||
public class GlobalExceptionHandler { | |||||
@ExceptionHandler(AppException.class) | |||||
public Result handleAppException(AppException e) { | |||||
return Result.error(e.getCode(), e.getMessage()); | |||||
} | |||||
// @ExceptionHandler(Exception.class) | |||||
// public Result handleException(Exception e) { | |||||
// return Result.error(Constants.SYSTEM_ERROR, e.getMessage()); | |||||
// } | |||||
} |
@ -0,0 +1,28 @@ | |||||
package cn.edu.ecnu.stu.bookstore.mapper; | |||||
import cn.edu.ecnu.stu.bookstore.pojo.User; | |||||
import org.apache.ibatis.annotations.Mapper; | |||||
import org.apache.ibatis.annotations.Param; | |||||
import org.apache.ibatis.annotations.Select; | |||||
import org.apache.ibatis.annotations.Update; | |||||
@Mapper | |||||
public interface UserMapper { | |||||
int insert(@Param("user") User user); | |||||
int delete(@Param("userId") int userId); | |||||
int checkUserByUsername(@Param("username") String username); | |||||
int deleteByName(@Param("username") String username); | |||||
@Select("select id, username, password, address, phone from t_user where username = #{username}") | |||||
User selectOneByName(@Param("username") String username); | |||||
@Select("select count(*) from t_user where username = #{username} and password = #{password}") | |||||
int checkPassword(@Param("username") String username, @Param("password") String password); | |||||
@Update("update t_user set password = #{password} where username = #{username}") | |||||
int updatePassword(@Param("username") String username, @Param("password") String password); | |||||
} |
@ -0,0 +1,53 @@ | |||||
package cn.edu.ecnu.stu.bookstore.pojo; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
import org.springframework.security.core.GrantedAuthority; | |||||
import org.springframework.security.core.userdetails.UserDetails; | |||||
import java.io.Serializable; | |||||
import java.util.Collection; | |||||
@Data | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
public class LoginUser implements UserDetails { | |||||
private User user; | |||||
@Override | |||||
public Collection<? extends GrantedAuthority> getAuthorities() { | |||||
return null; | |||||
} | |||||
@Override | |||||
public String getPassword() { | |||||
return user.getPassword(); | |||||
} | |||||
@Override | |||||
public String getUsername() { | |||||
return user.getUsername(); | |||||
} | |||||
@Override | |||||
public boolean isAccountNonExpired() { | |||||
return true; | |||||
} | |||||
@Override | |||||
public boolean isAccountNonLocked() { | |||||
return true; | |||||
} | |||||
@Override | |||||
public boolean isCredentialsNonExpired() { | |||||
return true; | |||||
} | |||||
@Override | |||||
public boolean isEnabled() { | |||||
return true; | |||||
} | |||||
} |
@ -0,0 +1,23 @@ | |||||
package cn.edu.ecnu.stu.bookstore.pojo; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
@Data | |||||
public class User { | |||||
private int id; | |||||
private String username; | |||||
private String password; | |||||
private int balance; | |||||
private String phone; | |||||
private String address; | |||||
} |
@ -0,0 +1,26 @@ | |||||
package cn.edu.ecnu.stu.bookstore.service; | |||||
import cn.edu.ecnu.stu.bookstore.component.AppException; | |||||
import cn.edu.ecnu.stu.bookstore.component.Constants; | |||||
import cn.edu.ecnu.stu.bookstore.pojo.LoginUser; | |||||
import cn.edu.ecnu.stu.bookstore.pojo.User; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.security.core.userdetails.UserDetails; | |||||
import org.springframework.security.core.userdetails.UserDetailsService; | |||||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | |||||
import org.springframework.stereotype.Service; | |||||
@Service | |||||
public class UserDetailsServiceImpl implements UserDetailsService { | |||||
@Autowired | |||||
private UserService userService; | |||||
@Override | |||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | |||||
User user = userService.selectUserByName(username); | |||||
if(user == null) | |||||
throw new AppException(Constants.CLIENT_ERROR, Constants.PARAMETER_ERROR_MESSAGE); | |||||
return new LoginUser(user); | |||||
} | |||||
} |
@ -0,0 +1,83 @@ | |||||
package cn.edu.ecnu.stu.bookstore.service; | |||||
import cn.edu.ecnu.stu.bookstore.component.AppException; | |||||
import cn.edu.ecnu.stu.bookstore.component.Constants; | |||||
import cn.edu.ecnu.stu.bookstore.mapper.UserMapper; | |||||
import cn.edu.ecnu.stu.bookstore.pojo.LoginUser; | |||||
import cn.edu.ecnu.stu.bookstore.pojo.User; | |||||
import cn.edu.ecnu.stu.bookstore.utils.JwtUtil; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.data.redis.core.RedisTemplate; | |||||
import org.springframework.security.authentication.AuthenticationManager; | |||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |||||
import org.springframework.security.core.Authentication; | |||||
import org.springframework.security.core.context.SecurityContextHolder; | |||||
import org.springframework.security.crypto.password.PasswordEncoder; | |||||
import org.springframework.stereotype.Service; | |||||
import org.springframework.util.StringUtils; | |||||
import java.util.concurrent.TimeUnit; | |||||
@Service | |||||
public class UserService { | |||||
@Autowired | |||||
private UserMapper userMapper; | |||||
@Autowired | |||||
private AuthenticationManager authenticationManager; | |||||
@Autowired | |||||
private PasswordEncoder passwordEncoder; | |||||
@Autowired | |||||
private RedisTemplate redisTemplate; | |||||
public void register(User user) { | |||||
if(!StringUtils.hasLength(user.getUsername())) | |||||
throw new AppException(Constants.CLIENT_ERROR, Constants.PARAMETER_ERROR_MESSAGE); | |||||
if(userMapper.checkUserByUsername(user.getUsername()) != 0) | |||||
throw new AppException(Constants.CLIENT_ERROR, Constants.REGISTER_ERROR_MESSAGE); | |||||
String encoded = passwordEncoder.encode(user.getPassword()); | |||||
user.setPassword(encoded); | |||||
userMapper.insert(user); | |||||
} | |||||
public void unregister(User user) { | |||||
User user1 = userMapper.selectOneByName(user.getUsername()); | |||||
if(user1 == null || !passwordEncoder.matches(user.getPassword(), user1.getPassword())) { | |||||
throw new AppException(Constants.CLIENT_ERROR, Constants.PASSWORD_ERROR); | |||||
} | |||||
userMapper.deleteByName(user.getUsername()); | |||||
} | |||||
public User selectUserByName(String username) { | |||||
return userMapper.selectOneByName(username); | |||||
} | |||||
public String login(User user) { | |||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()); | |||||
Authentication authenticate = authenticationManager.authenticate(token); | |||||
LoginUser loginUser = (LoginUser)authenticate.getPrincipal(); | |||||
redisTemplate.opsForValue().set("userId:" + loginUser.getUser().getId(), "1", 1, TimeUnit.DAYS); | |||||
return JwtUtil.getToken(loginUser.getUser()); | |||||
} | |||||
public void logout(String username) { | |||||
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); | |||||
if(!user.getUsername().equals(username)) { | |||||
throw new AppException(Constants.CLIENT_ERROR, Constants.PARAMETER_ERROR_MESSAGE); | |||||
} | |||||
redisTemplate.delete("userId:" + user.getId()); | |||||
} | |||||
public void changePassword(String username, String oldPassword, String newPassword) { | |||||
User user = userMapper.selectOneByName(username); | |||||
if(user == null || !passwordEncoder.matches(oldPassword, user.getPassword())){ | |||||
throw new AppException(Constants.CLIENT_ERROR, Constants.PASSWORD_ERROR); | |||||
} | |||||
newPassword = passwordEncoder.encode(newPassword); | |||||
userMapper.updatePassword(username, newPassword); | |||||
redisTemplate.delete("userId:" + user.getId()); | |||||
} | |||||
} |
@ -0,0 +1,50 @@ | |||||
package cn.edu.ecnu.stu.bookstore.utils; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.auth0.jwt.JWT; | |||||
import com.auth0.jwt.JWTCreator; | |||||
import com.auth0.jwt.algorithms.Algorithm; | |||||
import com.auth0.jwt.interfaces.DecodedJWT; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.data.redis.core.RedisTemplate; | |||||
import org.springframework.stereotype.Component; | |||||
import java.util.Calendar; | |||||
import java.util.Date; | |||||
import java.util.Map; | |||||
@Component | |||||
public class JwtUtil { | |||||
/** 盐值*/ | |||||
private static final String SING="!@#%^$*$%#^$#%^%$$#QWBADasda881"; | |||||
//生成令牌 | |||||
public static String getToken(Object object){ | |||||
//获取日历对象 | |||||
Calendar calendar= Calendar.getInstance(); | |||||
//默认1天过期 | |||||
calendar.add(Calendar.DATE,1); | |||||
//新建一个JWT的Builder对象 | |||||
JWTCreator.Builder builder = JWT.create(); | |||||
String jsonStr = JSON.toJSONString(object); | |||||
builder.withClaim("data",jsonStr); | |||||
//设置过期时间和签名 | |||||
return builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(SING)); | |||||
} | |||||
/** | |||||
* 验签并返回DecodedJWT | |||||
* @param token 令牌 | |||||
*/ | |||||
public static Object getTokenInfo(String token,Class<?> clazz){ | |||||
DecodedJWT jwt = JWT.require(Algorithm.HMAC256(SING)).build().verify(token); | |||||
return JSON.parseObject(jwt.getClaim("data").asString(), clazz); | |||||
} | |||||
public static Date getTokenExpiration(String token) { | |||||
DecodedJWT jwt = JWT.require(Algorithm.HMAC256(SING)).build().verify(token); | |||||
return jwt.getExpiresAt(); | |||||
} | |||||
} | |||||
@ -0,0 +1,103 @@ | |||||
package cn.edu.ecnu.stu.bookstore.utils; | |||||
import cn.edu.ecnu.stu.bookstore.component.Result; | |||||
import com.alibaba.fastjson.JSON; | |||||
import org.apache.http.HttpEntity; | |||||
import org.apache.http.HttpResponse; | |||||
import org.apache.http.client.HttpClient; | |||||
import org.apache.http.client.methods.HttpGet; | |||||
import org.apache.http.client.methods.HttpPost; | |||||
import org.apache.http.entity.StringEntity; | |||||
import org.apache.http.impl.client.HttpClients; | |||||
import org.apache.http.params.BasicHttpParams; | |||||
import org.apache.http.util.EntityUtils; | |||||
import java.io.IOException; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.Map; | |||||
public class RequestUtil { | |||||
public static Result post(String url, Map<String, Object> bodyMap, String token) { | |||||
// post请求 | |||||
HttpClient httpClient; | |||||
HttpPost httpPost; | |||||
HttpResponse response; | |||||
String responseContent; | |||||
String body = bodyMap == null ? null : JSON.toJSONString(bodyMap); | |||||
try { | |||||
// 创建 httpClient | |||||
httpClient = HttpClients.createDefault(); | |||||
httpPost = new HttpPost(url); | |||||
httpPost.addHeader("Accept", "*/*"); | |||||
httpPost.addHeader("Content-Type", "application/json;charset=utf8"); | |||||
if(token != null) | |||||
httpPost.addHeader("token", token); | |||||
// set entity | |||||
if(body != null) | |||||
httpPost.setEntity(new StringEntity(body, StandardCharsets.UTF_8)); | |||||
// 发送请求 | |||||
response = httpClient.execute(httpPost); | |||||
// 得到响应 | |||||
HttpEntity httpEntity = response.getEntity(); | |||||
responseContent = EntityUtils.toString(httpEntity, "UTF-8"); | |||||
// 释放资源 | |||||
EntityUtils.consume(httpEntity); | |||||
return JSON.parseObject(responseContent, Result.class); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
return null; | |||||
} | |||||
public static Result get(String url, Map<String, Object> params, String token) { | |||||
// post请求 | |||||
HttpClient httpClient; | |||||
HttpGet httpGet; | |||||
HttpResponse response; | |||||
String responseContent; | |||||
try { | |||||
// 创建 httpClient | |||||
httpClient = HttpClients.createDefault(); | |||||
httpGet = new HttpGet(url); | |||||
httpGet.addHeader("Accept", "*/*"); | |||||
httpGet.addHeader("Content-Type", "application/json;charset=utf8"); | |||||
if(token != null) | |||||
httpGet.addHeader("token", token); | |||||
// set params | |||||
if(params != null) { | |||||
BasicHttpParams httpParams = new BasicHttpParams(); | |||||
for (Map.Entry<String, Object> entry : params.entrySet()) { | |||||
httpParams.setParameter(entry.getKey(), entry.getValue()); | |||||
} | |||||
httpGet.setParams(httpParams); | |||||
} | |||||
// 发送请求 | |||||
response = httpClient.execute(httpGet); | |||||
// 得到响应 | |||||
HttpEntity httpEntity = response.getEntity(); | |||||
responseContent = EntityUtils.toString(httpEntity, "UTF-8"); | |||||
// 释放资源 | |||||
EntityUtils.consume(httpEntity); | |||||
return JSON.parseObject(responseContent, Result.class); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
return null; | |||||
} | |||||
} |
@ -0,0 +1,18 @@ | |||||
server: | |||||
port: 8080 | |||||
mybatis: | |||||
mapper-locations: classpath:mapper/*.xml | |||||
type-aliases-package: cn.edu.ecnu.stu.bookstore.pojo | |||||
spring: | |||||
datasource: | |||||
url: jdbc:mysql://localhost:3306/bookstore?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true | |||||
username: root | |||||
password: qwe030318 | |||||
driver-class-name: com.mysql.cj.jdbc.Driver | |||||
type: com.alibaba.druid.pool.DruidDataSource | |||||
redis: | |||||
host: 106.75.115.165 | |||||
port: 6379 | |||||
password: qwe030318 |
@ -0,0 +1,22 @@ | |||||
<!DOCTYPE mapper | |||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |||||
<mapper namespace="cn.edu.ecnu.stu.bookstore.mapper.UserMapper"> | |||||
<insert id="insert" useGeneratedKeys="true"> | |||||
insert into t_user(username, password, balance, address, phone) | |||||
values (#{user.username}, #{user.password}, #{user.balance}, #{user.address}, #{user.phone}) | |||||
</insert> | |||||
<delete id="delete"> | |||||
delete from t_user where id = #{userId} | |||||
</delete> | |||||
<delete id="deleteByName"> | |||||
delete from t_user where username = #{username} | |||||
</delete> | |||||
<select id="checkUserByUsername" resultType="integer"> | |||||
select count(*) from t_user where username = #{username} | |||||
</select> | |||||
</mapper> |
@ -0,0 +1,28 @@ | |||||
package cn.edu.ecnu.stu.bookstore; | |||||
import org.junit.jupiter.api.Test; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.boot.test.context.SpringBootTest; | |||||
import org.springframework.data.redis.core.RedisTemplate; | |||||
import org.springframework.data.redis.core.ValueOperations; | |||||
@SpringBootTest | |||||
class BookstoreApplicationTests { | |||||
@Test | |||||
void contextLoads() { | |||||
} | |||||
@Test | |||||
public void SetTest(@Autowired RedisTemplate redisTemplate){ | |||||
ValueOperations value = redisTemplate.opsForValue(); | |||||
value.set("springBoot","RedisOnSpringBoot"); | |||||
} | |||||
@Test | |||||
public void GetTest(@Autowired RedisTemplate redisTemplate){ | |||||
ValueOperations value = redisTemplate.opsForValue(); | |||||
Object o = value.get("springBoot"); | |||||
System.out.println(o); | |||||
} | |||||
} |
@ -0,0 +1,150 @@ | |||||
package cn.edu.ecnu.stu.bookstore; | |||||
import cn.edu.ecnu.stu.bookstore.component.Constants; | |||||
import cn.edu.ecnu.stu.bookstore.component.Result; | |||||
import cn.edu.ecnu.stu.bookstore.utils.RequestUtil; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import org.apache.http.HttpEntity; | |||||
import org.apache.http.HttpResponse; | |||||
import org.apache.http.client.HttpClient; | |||||
import org.apache.http.client.methods.HttpGet; | |||||
import org.apache.http.client.methods.HttpPost; | |||||
import org.apache.http.entity.StringEntity; | |||||
import org.apache.http.impl.client.HttpClients; | |||||
import org.apache.http.params.BasicHttpParams; | |||||
import org.apache.http.params.HttpParams; | |||||
import org.apache.http.util.EntityUtils; | |||||
import org.apache.tomcat.util.http.ResponseUtil; | |||||
import org.junit.jupiter.api.Test; | |||||
import org.springframework.boot.test.context.SpringBootTest; | |||||
import java.io.IOException; | |||||
import java.net.URL; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import java.util.Random; | |||||
import java.util.UUID; | |||||
@SpringBootTest | |||||
public class RegisterTest { | |||||
@Test | |||||
public void testRegister() { | |||||
String url = Constants.URL_PREFIX + "/auth/register"; | |||||
Map<String, Object> map = new HashMap<>(); | |||||
String username = UUID.randomUUID().toString(); | |||||
String password = username + "x"; | |||||
map.put("username", username); | |||||
map.put("password", password); | |||||
Result result = RequestUtil.post(url, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
map.put("username", username); | |||||
result = RequestUtil.post(url, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.CLIENT_ERROR); | |||||
} | |||||
@Test | |||||
public void testLogin() { | |||||
String url = Constants.URL_PREFIX + "/auth/register"; | |||||
Map<String, Object> map = new HashMap<>(); | |||||
String username = UUID.randomUUID().toString(); | |||||
String password = username + "x"; | |||||
map.put("username", username); | |||||
map.put("password", password); | |||||
Result result = RequestUtil.post(url, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
url = Constants.URL_PREFIX + "/auth/login"; | |||||
result = RequestUtil.post(url, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
map.put("password", password + "x"); | |||||
result = RequestUtil.post(url, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SYSTEM_ERROR); | |||||
} | |||||
@Test | |||||
public void testUnregister() { | |||||
String registerUrl = Constants.URL_PREFIX + "/auth/register"; | |||||
Map<String, Object> map = new HashMap<>(); | |||||
String username = UUID.randomUUID().toString(); | |||||
String password = username + "x"; | |||||
map.put("username", username); | |||||
map.put("password", password); | |||||
Result result = RequestUtil.post(registerUrl, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
String loginUrl = Constants.URL_PREFIX + "/auth/login"; | |||||
result = RequestUtil.post(loginUrl, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
String unregisterUrl = Constants.URL_PREFIX + "/auth/unregister"; | |||||
result = RequestUtil.post(unregisterUrl, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
result = RequestUtil.post(loginUrl, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.CLIENT_ERROR); | |||||
} | |||||
@Test | |||||
public void testLogout() { | |||||
String registerUrl = Constants.URL_PREFIX + "/auth/register"; | |||||
Map<String, Object> map = new HashMap<>(); | |||||
String username = UUID.randomUUID().toString(); | |||||
String password = username + "x"; | |||||
map.put("username", username); | |||||
map.put("password", password); | |||||
Result result = RequestUtil.post(registerUrl, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
String loginUrl = Constants.URL_PREFIX + "/auth/login"; | |||||
result = RequestUtil.post(loginUrl, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
JSONObject data = (JSONObject) result.getData(); | |||||
String token = data.getString("token"); | |||||
String testUrl = Constants.URL_PREFIX + "/test"; | |||||
result = RequestUtil.get(testUrl, null, token); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
String logoutUrl = Constants.URL_PREFIX + "/auth/logout"; | |||||
result = RequestUtil.post(logoutUrl, map, token); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
result = RequestUtil.get(testUrl, null, token); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.AUTHENTICATION_ERROR); | |||||
} | |||||
@Test | |||||
public void testPassword() { | |||||
String registerUrl = Constants.URL_PREFIX + "/auth/register"; | |||||
Map<String, Object> map = new HashMap<>(); | |||||
String username = UUID.randomUUID().toString(); | |||||
String password = username + "x"; | |||||
map.put("username", username); | |||||
map.put("password", password); | |||||
Result result = RequestUtil.post(registerUrl, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
String newPassword = password + "x"; | |||||
map.put("oldPassword", password); | |||||
map.put("newPassword", newPassword); | |||||
String passwordUrl = Constants.URL_PREFIX + "/auth/password"; | |||||
result = RequestUtil.post(passwordUrl, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
map.put("password", newPassword); | |||||
String loginUrl = Constants.URL_PREFIX + "/auth/login"; | |||||
result = RequestUtil.post(loginUrl, map, null); | |||||
assert result != null && result.getCode() != null && result.getCode().equals(Constants.SUCCESS); | |||||
} | |||||
} | |||||