feat:
- 实现Spring Security - 新增RoleMapper PermissionMapper
This commit is contained in:
parent
ee0109c876
commit
717395f8b3
17
pom.xml
17
pom.xml
@ -71,12 +71,12 @@
|
|||||||
<artifactId>jakarta.mail</artifactId>
|
<artifactId>jakarta.mail</artifactId>
|
||||||
<version>2.0.1</version>
|
<version>2.0.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<!-- <dependency>-->
|
||||||
<groupId>org.springframework.boot</groupId>
|
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||||
<artifactId>spring-boot-devtools</artifactId>
|
<!-- <artifactId>spring-boot-devtools</artifactId>-->
|
||||||
<scope>runtime</scope>
|
<!-- <scope>runtime</scope>-->
|
||||||
<optional>true</optional>
|
<!-- <optional>true</optional>-->
|
||||||
</dependency>
|
<!-- </dependency>-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
@ -127,6 +127,11 @@
|
|||||||
<artifactId>jjwt-jackson</artifactId>
|
<artifactId>jjwt-jackson</artifactId>
|
||||||
<version>0.11.5</version>
|
<version>0.11.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@ -13,11 +13,5 @@ import org.springframework.context.ConfigurableApplicationContext;
|
|||||||
public class WorkFlowMain {
|
public class WorkFlowMain {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ConfigurableApplicationContext context = SpringApplication.run(WorkFlowMain.class, args);
|
ConfigurableApplicationContext context = SpringApplication.run(WorkFlowMain.class, args);
|
||||||
int length = context.getBeanDefinitionCount();
|
|
||||||
log.trace("Number of beans in application context: {}", length);
|
|
||||||
log.debug("Number of beans in application context: {}", length);
|
|
||||||
log.info("Number of beans in application context: {}", length);
|
|
||||||
log.warn("Number of beans in application context: {}", length);
|
|
||||||
log.error("Number of beans in application context: {}", length);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,26 +1,61 @@
|
|||||||
package asia.yulinling.workflow.config;
|
package asia.yulinling.workflow.config;
|
||||||
|
|
||||||
|
|
||||||
|
import asia.yulinling.workflow.model.vo.user.UserPrincipal;
|
||||||
|
import asia.yulinling.workflow.security.AuthEntryPoint;
|
||||||
|
import asia.yulinling.workflow.security.JwtAuthenticationFilter;
|
||||||
|
import asia.yulinling.workflow.service.CustomerDetailsService;
|
||||||
|
import asia.yulinling.workflow.utils.JwtUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.config.Customizer;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
private final CustomerDetailsService customerDetailService;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtUtil jwtUtil, AuthEntryPoint authEntryPoint, RequestMatcher requestMatcher) throws Exception {
|
||||||
http.authorizeHttpRequests(authorize -> authorize
|
http
|
||||||
.requestMatchers("/users", "/users/**").permitAll()
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
.anyRequest().authenticated())
|
.exceptionHandling(ex -> ex
|
||||||
.formLogin(formLogin -> formLogin
|
.authenticationEntryPoint(authEntryPoint)
|
||||||
.loginPage("/login")
|
.accessDeniedHandler((request, response, accessDeniedException) -> response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied"))
|
||||||
.permitAll())
|
)
|
||||||
.rememberMe(Customizer.withDefaults());
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
.requestMatchers("/login").permitAll()
|
||||||
|
.requestMatchers("/users", "/users/**").authenticated()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
.sessionManagement(AbstractHttpConfigurer::disable)
|
||||||
|
.addFilterBefore(
|
||||||
|
new JwtAuthenticationFilter(jwtUtil, authEntryPoint, requestMatcher),
|
||||||
|
UsernamePasswordAuthenticationFilter.class
|
||||||
|
);
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,4 +66,32 @@ public class SecurityConfig {
|
|||||||
public PasswordEncoder passwordEncoder() {
|
public PasswordEncoder passwordEncoder() {
|
||||||
return new BCryptPasswordEncoder();
|
return new BCryptPasswordEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean RequestMatcher requestMatcher() {
|
||||||
|
return new AntPathRequestMatcher("/login", "POST");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DaoAuthenticationProvider authenticationProvider() {
|
||||||
|
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||||
|
authProvider.setUserDetailsService(customerDetailService);
|
||||||
|
return authProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
|
||||||
|
UserPrincipal user = new UserPrincipal(
|
||||||
|
1L,
|
||||||
|
"admin",
|
||||||
|
passwordEncoder.encode("123456"),
|
||||||
|
List.of("ADMIN"),
|
||||||
|
List.of(new SimpleGrantedAuthority("ROLE_ADMIN"))
|
||||||
|
);
|
||||||
|
return new InMemoryUserDetailsManager(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
|
||||||
|
return configuration.getAuthenticationManager();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,51 @@
|
|||||||
|
package asia.yulinling.workflow.controller;
|
||||||
|
|
||||||
|
import asia.yulinling.workflow.dto.request.LoginRequest;
|
||||||
|
import asia.yulinling.workflow.utils.JwtUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 登录控制层
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author YLL
|
||||||
|
* @since 2025/6/13
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class LoginController {
|
||||||
|
|
||||||
|
private final AuthenticationManager authenticationManager;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@PostMapping("/login")
|
||||||
|
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
|
||||||
|
try {
|
||||||
|
UsernamePasswordAuthenticationToken authToken =
|
||||||
|
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
|
||||||
|
|
||||||
|
Authentication authentication = authenticationManager.authenticate(authToken);
|
||||||
|
|
||||||
|
log.info("{}", authentication.getPrincipal());
|
||||||
|
String token = jwtUtil.generateToken(authentication, false); // 可根据需要传 true/false
|
||||||
|
|
||||||
|
return ResponseEntity.ok(token);
|
||||||
|
} catch (AuthenticationException ex) {
|
||||||
|
log.info("{}", ex.getMessage());
|
||||||
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -8,10 +8,12 @@ import asia.yulinling.workflow.model.ApiResponse;
|
|||||||
import asia.yulinling.workflow.dto.response.PageResult;
|
import asia.yulinling.workflow.dto.response.PageResult;
|
||||||
import asia.yulinling.workflow.model.vo.user.UserVO;
|
import asia.yulinling.workflow.model.vo.user.UserVO;
|
||||||
import asia.yulinling.workflow.service.UserService;
|
import asia.yulinling.workflow.service.UserService;
|
||||||
|
import asia.yulinling.workflow.utils.JwtUtil;
|
||||||
import cn.hutool.core.lang.Dict;
|
import cn.hutool.core.lang.Dict;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -29,6 +31,8 @@ import java.util.Map;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class TestController {
|
public class TestController {
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
private final AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 测试方法 GET
|
* 测试方法 GET
|
||||||
@ -72,9 +76,4 @@ public class TestController {
|
|||||||
public ApiResponse<PageResult<UserVO>> usersPage(PageParam pageParam) {
|
public ApiResponse<PageResult<UserVO>> usersPage(PageParam pageParam) {
|
||||||
return userService.getUserListByPage(pageParam);
|
return userService.getUserListByPage(pageParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/login")
|
|
||||||
public ApiResponse<String> login() {
|
|
||||||
return ApiResponse.ofSuccess("登录成功");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,17 @@
|
|||||||
|
package asia.yulinling.workflow.dto.request;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 登录请求类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author YLL
|
||||||
|
* @since 2025/6/13
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class LoginRequest {
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package asia.yulinling.workflow.mapper;
|
||||||
|
|
||||||
|
import asia.yulinling.workflow.model.entity.Permission;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author YLL
|
||||||
|
* @since 2025/6/13
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
@Component
|
||||||
|
public interface PermissionMapper extends BaseMapper<Permission> {
|
||||||
|
}
|
||||||
19
src/main/java/asia/yulinling/workflow/mapper/RoleMapper.java
Normal file
19
src/main/java/asia/yulinling/workflow/mapper/RoleMapper.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package asia.yulinling.workflow.mapper;
|
||||||
|
|
||||||
|
import asia.yulinling.workflow.model.entity.Role;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 角色Mapper
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author YLL
|
||||||
|
* @since 2025/6/13
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
@Component
|
||||||
|
public interface RoleMapper extends BaseMapper<Role> {
|
||||||
|
}
|
||||||
@ -3,8 +3,6 @@ package asia.yulinling.workflow.mapper;
|
|||||||
import asia.yulinling.workflow.model.entity.User;
|
import asia.yulinling.workflow.model.entity.User;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
import org.apache.ibatis.annotations.Select;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,29 +16,4 @@ import org.springframework.stereotype.Component;
|
|||||||
@Mapper
|
@Mapper
|
||||||
@Component
|
@Component
|
||||||
public interface UserMapper extends BaseMapper<User> {
|
public interface UserMapper extends BaseMapper<User> {
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据id查询用户
|
|
||||||
*
|
|
||||||
* @param id 主键id
|
|
||||||
* @return 当前id的用户,不存在则是{@code null}
|
|
||||||
*/
|
|
||||||
@Select("SELECT * FROM orm_user WHERE id = #{id}")
|
|
||||||
User selectUserById(@Param("id")Long id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存用户
|
|
||||||
*
|
|
||||||
* @param user 用户
|
|
||||||
* @return 成功 - {@code 1} 失败 - {@code 0}
|
|
||||||
*/
|
|
||||||
int saveUser(@Param("user") User user);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除用户
|
|
||||||
*
|
|
||||||
* @param id 主键id
|
|
||||||
* @return 成功 - {@code 1} 失败 - {@code 0}
|
|
||||||
*/
|
|
||||||
int deleteById(@Param("id")Long id);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,23 @@
|
|||||||
package asia.yulinling.workflow.model.vo.user;
|
package asia.yulinling.workflow.model.vo.user;
|
||||||
|
|
||||||
|
import asia.yulinling.workflow.model.entity.Permission;
|
||||||
|
import asia.yulinling.workflow.model.entity.Role;
|
||||||
|
import asia.yulinling.workflow.model.entity.User;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -49,12 +54,6 @@ public class UserPrincipal implements UserDetails {
|
|||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
/**
|
|
||||||
* 加密使用盐
|
|
||||||
*/
|
|
||||||
@JsonIgnore
|
|
||||||
private String salt;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 邮箱
|
* 邮箱
|
||||||
*/
|
*/
|
||||||
@ -98,25 +97,80 @@ public class UserPrincipal implements UserDetails {
|
|||||||
/**
|
/**
|
||||||
* 用户角色列表
|
* 用户角色列表
|
||||||
*/
|
*/
|
||||||
private List<String > roles;
|
private List<String> roles;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户权限列表
|
* 用户权限列表
|
||||||
*/
|
*/
|
||||||
private Collection<? extends GrantedAuthority> authorities;
|
private Collection<? extends GrantedAuthority> authorities;
|
||||||
|
|
||||||
|
public UserPrincipal(Long id, String username, String password, String nickname, String phone, String email, String birthday, Integer sex, Integer status, Date createTime, Date updateTime, List<String> roleNames, List<GrantedAuthority> authorities) {
|
||||||
|
this.id = id;
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.nickname = nickname;
|
||||||
|
this.phone = phone;
|
||||||
|
this.email = email;
|
||||||
|
this.birthday = birthday;
|
||||||
|
this.sex = sex;
|
||||||
|
this.status = status;
|
||||||
|
this.createTime = createTime;
|
||||||
|
this.updateTime = updateTime;
|
||||||
|
this.roles = roleNames;
|
||||||
|
this.authorities = authorities;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserPrincipal(Long id, String username, String password,
|
||||||
|
List<String> roles, Collection<? extends GrantedAuthority> authorities) {
|
||||||
|
this.id = id;
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.roles = roles;
|
||||||
|
this.authorities = authorities;
|
||||||
|
this.status = 1; // 启用
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserPrincipal create(User user, List<Role> roles, List<Permission> permissions) {
|
||||||
|
List<String> roleNames = roles.stream().map(Role::getName).collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<GrantedAuthority> authorities = permissions.stream().filter(permission -> StrUtil.isNotBlank(permission.getPermission())).map(permission -> new SimpleGrantedAuthority(permission.getPermission())).collect(Collectors.toList());
|
||||||
|
|
||||||
|
return new UserPrincipal(user.getId(), user.getUsername(), user.getPassword(), user.getNickname(), user.getPhone(), user.getEmail(), user.getBirthday(), user.getSex(), user.getStatus(), user.getCreateTime(), user.getUpdateTime(), roleNames, authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return "";
|
return this.password;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return "";
|
return this.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonExpired() {
|
||||||
|
return true; // 视业务逻辑可改
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonLocked() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCredentialsNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return this.status != null && this.status == 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
package asia.yulinling.workflow.security;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 权限不足处理器
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author YLL
|
||||||
|
* @since 2025/6/13
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class AuthEntryPoint implements AuthenticationEntryPoint {
|
||||||
|
@Override
|
||||||
|
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
AuthenticationException authException) throws IOException {
|
||||||
|
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Missing or invalid token");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
package asia.yulinling.workflow.security;
|
||||||
|
|
||||||
|
import asia.yulinling.workflow.utils.JwtUtil;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Jwt登录拦截器
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author YLL
|
||||||
|
* @since 2025/6/13
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
private final AuthenticationEntryPoint authenticationEntryPoint;
|
||||||
|
private final RequestMatcher requestMatcher;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
FilterChain filterChain
|
||||||
|
)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
log.info("{}", request.getRequestURI());
|
||||||
|
if (requestMatcher.matches(request)) {
|
||||||
|
log.info("{}", request.getRequestURI());
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String header = request.getHeader("Authorization");
|
||||||
|
if (header == null || !header.startsWith("Bearer ")) {
|
||||||
|
String token = null;
|
||||||
|
if (header != null) {
|
||||||
|
token = header.substring(7);
|
||||||
|
}
|
||||||
|
if (jwtUtil.validateToken(token)) {
|
||||||
|
String username = jwtUtil.parseToken(token).getSubject();
|
||||||
|
String roles = jwtUtil.parseToken(token).get("roles").toString();
|
||||||
|
Authentication auth = new JwtAuthenticationToken(username, roles);
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||||
|
} else {
|
||||||
|
authenticationEntryPoint.commence(request, response, new AuthenticationException("") {
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package asia.yulinling.workflow.security;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* JWT认证
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author YLL
|
||||||
|
* @since 2025/6/13
|
||||||
|
*/
|
||||||
|
public class JwtAuthenticationToken extends AbstractAuthenticationToken {
|
||||||
|
|
||||||
|
private final String principal;
|
||||||
|
private final String role;
|
||||||
|
|
||||||
|
public JwtAuthenticationToken(String username, String role) {
|
||||||
|
super(Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role)));
|
||||||
|
this.principal = username;
|
||||||
|
this.role = role;
|
||||||
|
setAuthenticated(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPrincipal() {
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCredentials() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package asia.yulinling.workflow.service;
|
||||||
|
|
||||||
|
import asia.yulinling.workflow.mapper.PermissionMapper;
|
||||||
|
import asia.yulinling.workflow.mapper.RoleMapper;
|
||||||
|
import asia.yulinling.workflow.mapper.UserMapper;
|
||||||
|
import asia.yulinling.workflow.model.entity.Permission;
|
||||||
|
import asia.yulinling.workflow.model.entity.Role;
|
||||||
|
import asia.yulinling.workflow.model.entity.User;
|
||||||
|
import asia.yulinling.workflow.model.vo.user.UserPrincipal;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 自定义UserDetail查询
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author YLL
|
||||||
|
* @since 2025/6/13
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class CustomerDetailsService implements UserDetailsService {
|
||||||
|
private final UserMapper userMapper;
|
||||||
|
private final RoleMapper roleMapper;
|
||||||
|
private final PermissionMapper permissionMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class)
|
||||||
|
.eq(User::getUsername, username)
|
||||||
|
.or()
|
||||||
|
.eq(User::getEmail, username)
|
||||||
|
.or()
|
||||||
|
.eq(User::getPhone, username);
|
||||||
|
|
||||||
|
User user = userMapper.selectOne(queryWrapper);
|
||||||
|
List<Role> roles = roleMapper.selectByIds(Collections.singleton(user.getId()));
|
||||||
|
List<Long> roleIds = roles.stream().map(Role::getId).collect(Collectors.toList());
|
||||||
|
List<Permission> permissions = permissionMapper.selectByIds(roleIds);
|
||||||
|
return UserPrincipal.create(user, roles, permissions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ import asia.yulinling.workflow.dto.response.PageResult;
|
|||||||
import asia.yulinling.workflow.model.entity.User;
|
import asia.yulinling.workflow.model.entity.User;
|
||||||
import asia.yulinling.workflow.model.vo.user.UserVO;
|
import asia.yulinling.workflow.model.vo.user.UserVO;
|
||||||
import asia.yulinling.workflow.service.UserService;
|
import asia.yulinling.workflow.service.UserService;
|
||||||
import asia.yulinling.workflow.utils.PageUtils;
|
import asia.yulinling.workflow.utils.PageUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -74,7 +74,7 @@ public class UserServiceImpl implements UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
PageResult<UserVO> pageResult = PageUtils.buildPageResult(users, userVOList);
|
PageResult<UserVO> pageResult = PageUtil.buildPageResult(users, userVOList);
|
||||||
|
|
||||||
return ApiResponse.ofSuccess(pageResult);
|
return ApiResponse.ofSuccess(pageResult);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,11 +6,12 @@ import io.jsonwebtoken.security.Keys;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import io.jsonwebtoken.*;
|
import io.jsonwebtoken.*;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -25,8 +26,8 @@ import java.util.List;
|
|||||||
* @since 2025/6/11
|
* @since 2025/6/11
|
||||||
*/
|
*/
|
||||||
@EnableConfigurationProperties(JwtConfig.class)
|
@EnableConfigurationProperties(JwtConfig.class)
|
||||||
@Configuration
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class JwtUtil {
|
public class JwtUtil {
|
||||||
private final JwtConfig jwtConfig;
|
private final JwtConfig jwtConfig;
|
||||||
@ -34,30 +35,35 @@ public class JwtUtil {
|
|||||||
/**
|
/**
|
||||||
* 创建JWT
|
* 创建JWT
|
||||||
*
|
*
|
||||||
* @param rememberMe 记住我
|
* @param isRememberMe 记住我
|
||||||
* @param id 用户id
|
* @param userId 用户id
|
||||||
* @param subject 用户名
|
* @param subject 用户名
|
||||||
* @param roles 用户角色
|
* @param roles 用户角色
|
||||||
* @param authorities 用户权限
|
* @param authorities 用户权限
|
||||||
* @return JWT
|
* @return JWT
|
||||||
*/
|
*/
|
||||||
public String createJWT(Boolean rememberMe, Long id, String subject, List<String> roles, Collection<? extends GrantedAuthority> authorities) {
|
public String generateToken(boolean isRememberMe, Long userId, String subject, List<String> roles, Collection<? extends GrantedAuthority> authorities) {
|
||||||
|
// 1. 构建签名密钥
|
||||||
|
Key key = getSigningKey();
|
||||||
|
|
||||||
|
// 2. 当前时间
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
|
|
||||||
Key key = Keys.hmacShaKeyFor(jwtConfig.getKey().getBytes());
|
// 3. 计算过期时间
|
||||||
|
long ttl = isRememberMe ? jwtConfig.getRemember() : jwtConfig.getTtl();
|
||||||
|
Date expiration = new Date(now.getTime() + ttl);
|
||||||
|
|
||||||
|
// 4. 构建 JWT
|
||||||
JwtBuilder builder = Jwts.builder()
|
JwtBuilder builder = Jwts.builder()
|
||||||
.setId(id.toString())
|
.setId(String.valueOf(userId))
|
||||||
.setSubject(subject)
|
.setSubject(subject)
|
||||||
.setIssuedAt(now)
|
.setIssuedAt(now)
|
||||||
.signWith(key)
|
.setExpiration(expiration)
|
||||||
.claim("roles", roles)
|
.claim("roles", roles)
|
||||||
.claim("authorities", authorities);
|
.claim("authorities", authorities)
|
||||||
|
.signWith(key);
|
||||||
// 设置过期时间
|
|
||||||
Long ttl = rememberMe ? jwtConfig.getRemember() : jwtConfig.getTtl();
|
|
||||||
builder.setExpiration(new Date(now.getTime() + ttl));
|
|
||||||
|
|
||||||
|
// 5. 返回生成的 token 字符串
|
||||||
return builder.compact();
|
return builder.compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,13 +71,13 @@ public class JwtUtil {
|
|||||||
* 创建JWT
|
* 创建JWT
|
||||||
*
|
*
|
||||||
* @param authentication 用户认证信息
|
* @param authentication 用户认证信息
|
||||||
* @param rememberMe 记住我
|
* @param isRememberMe 记住我
|
||||||
* @return JWT
|
* @return JWT
|
||||||
*/
|
*/
|
||||||
public String createJWT(Authentication authentication, Boolean rememberMe) {
|
public String generateToken(Authentication authentication, Boolean isRememberMe) {
|
||||||
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
|
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
|
||||||
return createJWT(
|
return generateToken(
|
||||||
rememberMe,
|
isRememberMe,
|
||||||
userPrincipal.getId(),
|
userPrincipal.getId(),
|
||||||
userPrincipal.getUsername(),
|
userPrincipal.getUsername(),
|
||||||
userPrincipal.getRoles(),
|
userPrincipal.getRoles(),
|
||||||
@ -79,25 +85,58 @@ public class JwtUtil {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Claims parseJWT(String jwt) {
|
/**
|
||||||
|
* 解析Token
|
||||||
|
*
|
||||||
|
* @param token token信息
|
||||||
|
* @return 解析信息
|
||||||
|
*/
|
||||||
|
public Claims parseToken(String token) {
|
||||||
try {
|
try {
|
||||||
// 1. 构建密钥
|
Key key = getSigningKey();
|
||||||
Key key = Keys.hmacShaKeyFor(jwtConfig.getKey().getBytes());
|
|
||||||
|
|
||||||
// 2. 使用 parserBuilder 构建解析器
|
return Jwts.parserBuilder()
|
||||||
Jws<Claims> jws = Jwts.parserBuilder()
|
|
||||||
.setSigningKey(key)
|
.setSigningKey(key)
|
||||||
.build()
|
.build()
|
||||||
.parseClaimsJws(jwt);
|
.parseClaimsJws(token)
|
||||||
|
.getBody();
|
||||||
Claims claims = jws.getBody();
|
|
||||||
String id = claims.getId();
|
|
||||||
String username = claims.getSubject();
|
|
||||||
|
|
||||||
return claims;
|
|
||||||
} catch (ExpiredJwtException e) {
|
} catch (ExpiredJwtException e) {
|
||||||
log.error("ExpiredJwtException", e);
|
log.error("Token 已过期: {}", token, e);
|
||||||
throw new JwtException("ExpiredJwtException");
|
throw new JwtException("Token 已过期", e);
|
||||||
|
} catch (JwtException e) {
|
||||||
|
log.error("Token 无效: {}", token, e);
|
||||||
|
throw new JwtException("Token 无效", e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Token 解析异常: {}", token, e);
|
||||||
|
throw new JwtException("Token 解析失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验Token
|
||||||
|
*
|
||||||
|
* @param token token信息
|
||||||
|
* @return true-token正确 false-token错误
|
||||||
|
*/
|
||||||
|
public boolean validateToken(String token) {
|
||||||
|
try {
|
||||||
|
Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token);
|
||||||
|
return true;
|
||||||
|
} catch (JwtException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取签名密钥
|
||||||
|
*
|
||||||
|
* @return 返回key
|
||||||
|
*/
|
||||||
|
private Key getSigningKey() {
|
||||||
|
String secret = jwtConfig.getKey();
|
||||||
|
if (secret == null || secret.isEmpty()) {
|
||||||
|
throw new IllegalStateException("JWT 签名密钥未配置");
|
||||||
|
}
|
||||||
|
return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,13 +7,13 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* 分页工具类
|
* 分页工具类
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author YLL
|
* @author YLL
|
||||||
* @since 2025/6/9
|
* @since 2025/6/9
|
||||||
*/
|
*/
|
||||||
public class PageUtils {
|
public class PageUtil {
|
||||||
public static <T> PageResult<T> buildPageResult(IPage<?> iPage, List<T> list) {
|
public static <T> PageResult<T> buildPageResult(IPage<?> iPage, List<T> list) {
|
||||||
PageResult<T> result = new PageResult<>();
|
PageResult<T> result = new PageResult<>();
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ public class PageUtils {
|
|||||||
// 可选字段
|
// 可选字段
|
||||||
int navigatePages = 5;
|
int navigatePages = 5;
|
||||||
result.setNavigatePages(navigatePages);
|
result.setNavigatePages(navigatePages);
|
||||||
result.setNavigatePageNums(calculateNavigatePageNumbers((int) iPage.getCurrent(), (int)iPage.getPages(), navigatePages));
|
result.setNavigatePageNums(calculateNavigatePageNumbers((int) iPage.getCurrent(), (int) iPage.getPages(), navigatePages));
|
||||||
|
|
||||||
result.setHasNext(iPage.getCurrent() < iPage.getPages());
|
result.setHasNext(iPage.getCurrent() < iPage.getPages());
|
||||||
result.setHasPrev(iPage.getCurrent() > 1);
|
result.setHasPrev(iPage.getCurrent() > 1);
|
||||||
@ -36,7 +36,7 @@ public class PageUtils {
|
|||||||
result.setFrom((int) ((iPage.getCurrent() - 1) * iPage.getSize() + 1));
|
result.setFrom((int) ((iPage.getCurrent() - 1) * iPage.getSize() + 1));
|
||||||
result.setTo((int) (Math.min(iPage.getCurrent() * iPage.getSize(), iPage.getTotal())));
|
result.setTo((int) (Math.min(iPage.getCurrent() * iPage.getSize(), iPage.getTotal())));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] calculateNavigatePageNumbers(int currentPage, int totalPage, int navigatePages) {
|
private static int[] calculateNavigatePageNumbers(int currentPage, int totalPage, int navigatePages) {
|
||||||
@ -46,14 +46,13 @@ COMMIT;
|
|||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
||||||
INSERT INTO `wk_user` (
|
INSERT INTO `wk_user` (
|
||||||
id, username, nickname, password, salt, email, birthday, sex, phone, status,
|
id, username, nickname, password, email, birthday, sex, phone, status,
|
||||||
create_time, update_time, last_login_time
|
create_time, update_time, last_login_time
|
||||||
) VALUES (
|
) VALUES (
|
||||||
1072806377661009920,
|
1072806377661009920,
|
||||||
'admin',
|
'admin',
|
||||||
'管理员',
|
'管理员',
|
||||||
'ff342e862e7c3285cdc07e56d6b8973b',
|
'ff342e862e7c3285cdc07e56d6b8973b',
|
||||||
'412365a109674b2dbb1981ed561a4c70',
|
|
||||||
'admin@xkcoding.com',
|
'admin@xkcoding.com',
|
||||||
'1994-11-28 00:00:00', -- birthday: 785433600000 → 1994-11-28
|
'1994-11-28 00:00:00', -- birthday: 785433600000 → 1994-11-28
|
||||||
1,
|
1,
|
||||||
@ -65,14 +64,13 @@ INSERT INTO `wk_user` (
|
|||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO `wk_user` (
|
INSERT INTO `wk_user` (
|
||||||
id, username, nickname, password, salt, email, birthday, sex, phone, status,
|
id, username, nickname, password, email, birthday, sex, phone, status,
|
||||||
create_time, update_time, last_login_time
|
create_time, update_time, last_login_time
|
||||||
) VALUES (
|
) VALUES (
|
||||||
1072806378780889088,
|
1072806378780889088,
|
||||||
'user',
|
'user',
|
||||||
'普通用户',
|
'普通用户',
|
||||||
'6c6bf02c8d5d3d128f34b1700cb1e32c',
|
'6c6bf02c8d5d3d128f34b1700cb1e32c',
|
||||||
'fcbdd0e8a9404a5585ea4e01d0e4d7a0',
|
|
||||||
'user@xkcoding.com',
|
'user@xkcoding.com',
|
||||||
'1994-11-28 00:00:00', -- birthday: 785433600000 → 1994-11-28
|
'1994-11-28 00:00:00', -- birthday: 785433600000 → 1994-11-28
|
||||||
1,
|
1,
|
||||||
|
|||||||
@ -59,7 +59,6 @@ public class UserMapperTest {
|
|||||||
User user = User.builder()
|
User user = User.builder()
|
||||||
.username("yulinling_test")
|
.username("yulinling_test")
|
||||||
.password(SecureUtil.md5("123456" + salt))
|
.password(SecureUtil.md5("123456" + salt))
|
||||||
.salt(salt)
|
|
||||||
.email("2712495353@qq.com")
|
.email("2712495353@qq.com")
|
||||||
.phone("17770898274")
|
.phone("17770898274")
|
||||||
.status(1)
|
.status(1)
|
||||||
|
|||||||
109
src/test/java/asia/yulinling/workflow/utils/JwtUtilTest.java
Normal file
109
src/test/java/asia/yulinling/workflow/utils/JwtUtilTest.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package asia.yulinling.workflow.utils;
|
||||||
|
|
||||||
|
import asia.yulinling.workflow.config.JwtConfig;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Jwt工具类测试
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author YLL
|
||||||
|
* @since 2025/6/13
|
||||||
|
*/
|
||||||
|
class JwtUtilTest {
|
||||||
|
private JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
// 构造 JwtConfig 模拟配置
|
||||||
|
JwtConfig config = new JwtConfig();
|
||||||
|
config.setKey("your-256-bit-secret-key-which-needs-to-be-long-enough"); // 至少256位bit
|
||||||
|
config.setTtl(3600000L); // 1小时
|
||||||
|
config.setRemember(604800000L); // 7天
|
||||||
|
|
||||||
|
jwtUtil = new JwtUtil(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGenerateTokenAndParseToken() {
|
||||||
|
Long userId = 123L;
|
||||||
|
String username = "test_user";
|
||||||
|
List<String> roles = List.of("USER", "ADMIN");
|
||||||
|
List<SimpleGrantedAuthority> authorities = List.of(new SimpleGrantedAuthority("ROLE_USER"));
|
||||||
|
|
||||||
|
String token = jwtUtil.generateToken(false, userId, username, roles, authorities);
|
||||||
|
assertNotNull(token);
|
||||||
|
|
||||||
|
Claims claims = jwtUtil.parseToken(token);
|
||||||
|
assertEquals(userId.toString(), claims.getId());
|
||||||
|
assertEquals(username, claims.getSubject());
|
||||||
|
assertEquals(roles, claims.get("roles", List.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidateToken() {
|
||||||
|
String token = jwtUtil.generateToken(false, 1L, "valid_user", List.of("USER"), List.of());
|
||||||
|
assertTrue(jwtUtil.validateToken(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidateToken2() {
|
||||||
|
String invalidToken = "this.is.not.valid.jwt";
|
||||||
|
assertFalse(jwtUtil.validateToken(invalidToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGenerateTokenFromAuthentication() {
|
||||||
|
UsernamePasswordAuthenticationToken authentication =
|
||||||
|
new UsernamePasswordAuthenticationToken(
|
||||||
|
new MockUserPrincipal(100L, "admin", List.of("ADMIN")),
|
||||||
|
null,
|
||||||
|
List.of(new SimpleGrantedAuthority("ROLE_ADMIN"))
|
||||||
|
);
|
||||||
|
|
||||||
|
String token = jwtUtil.generateToken(authentication, false);
|
||||||
|
assertNotNull(token);
|
||||||
|
|
||||||
|
Claims claims = jwtUtil.parseToken(token);
|
||||||
|
assertEquals("admin", claims.getSubject());
|
||||||
|
assertEquals("100", claims.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock UserPrincipal 用于测试
|
||||||
|
static class MockUserPrincipal extends asia.yulinling.workflow.model.vo.user.UserPrincipal {
|
||||||
|
private final Long id;
|
||||||
|
private final String username;
|
||||||
|
private final List<String> roles;
|
||||||
|
|
||||||
|
public MockUserPrincipal(Long id, String username, List<String> roles) {
|
||||||
|
super(); // 假设父类有默认构造函数
|
||||||
|
this.id = id;
|
||||||
|
this.username = username;
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user