@ -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); | |||
} | |||
} | |||