diff --git a/src/main/java/asia/yulinling/workflow/config/JwtConfig.java b/src/main/java/asia/yulinling/workflow/config/JwtConfig.java deleted file mode 100644 index 1d67657..0000000 --- a/src/main/java/asia/yulinling/workflow/config/JwtConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -package asia.yulinling.workflow.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - *
- * JWT配置类 - *
- * - * @author YLL - * @since 2025/6/11 - */ -@ConfigurationProperties(prefix = "jwt.config") -@Data -public class JwtConfig { - /** - * jwt 加密 key,默认值:kw. - */ - private String key = "kw"; - - /** - * jwt 过期时间,默认值:600000 {@code 10 分钟}. - */ - private Long ttl = 600000L; - - /** - * 开启 记住我 之后 jwt 过期时间,默认值 604800000 {@code 7 天} - */ - private Long remember = 604800000L; -} diff --git a/src/main/java/asia/yulinling/workflow/config/SecurityConfig.java b/src/main/java/asia/yulinling/workflow/config/SecurityConfig.java index 00673c5..2ab1fb2 100644 --- a/src/main/java/asia/yulinling/workflow/config/SecurityConfig.java +++ b/src/main/java/asia/yulinling/workflow/config/SecurityConfig.java @@ -1,16 +1,12 @@ package asia.yulinling.workflow.config; - -import asia.yulinling.workflow.model.vo.user.UserPrincipal; -import asia.yulinling.workflow.security.AuthEntryPoint; +import asia.yulinling.workflow.security.JwtAuthenticationEntryPoint; import asia.yulinling.workflow.security.JwtAuthenticationFilter; -import asia.yulinling.workflow.service.CustomerDetailsService; +import asia.yulinling.workflow.security.JwtUserDetailsService; 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.Configuration; import org.springframework.security.authentication.AuthenticationManager; @@ -18,16 +14,10 @@ import org.springframework.security.config.annotation.authentication.configurati 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.configurers.AbstractHttpConfigurer; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; 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 @EnableWebSecurity @@ -35,14 +25,14 @@ import java.util.List; @Slf4j public class SecurityConfig { - private final CustomerDetailsService customerDetailService; + private final JwtUserDetailsService jwtUserDetailsService; @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtUtil jwtUtil, AuthEntryPoint authEntryPoint, RequestMatcher requestMatcher) throws Exception { + public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtUtil jwtUtil, JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .exceptionHandling(ex -> ex - .authenticationEntryPoint(authEntryPoint) + .authenticationEntryPoint(jwtAuthenticationEntryPoint) .accessDeniedHandler((request, response, accessDeniedException) -> response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied")) ) .authorizeHttpRequests(auth -> auth @@ -52,9 +42,10 @@ public class SecurityConfig { ) .sessionManagement(AbstractHttpConfigurer::disable) .addFilterBefore( - new JwtAuthenticationFilter(jwtUtil, authEntryPoint, requestMatcher), + new JwtAuthenticationFilter(jwtUtil, jwtUserDetailsService), UsernamePasswordAuthenticationFilter.class - ); + ) + .userDetailsService(jwtUserDetailsService); return http.build(); } @@ -67,29 +58,6 @@ public class SecurityConfig { 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(); diff --git a/src/main/java/asia/yulinling/workflow/controller/AuthController.java b/src/main/java/asia/yulinling/workflow/controller/AuthController.java new file mode 100644 index 0000000..189b28c --- /dev/null +++ b/src/main/java/asia/yulinling/workflow/controller/AuthController.java @@ -0,0 +1,33 @@ +package asia.yulinling.workflow.controller; + +import asia.yulinling.workflow.dto.request.LoginRequest; +import asia.yulinling.workflow.service.AuthService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +/** + *+ * 登录控制层 + *
+ * + * @author YLL + * @since 2025/6/13 + */ +@RestController +@RequiredArgsConstructor +@Slf4j +public class AuthController { + + private final AuthService authenticate; + + @PostMapping("/login") + public ResponseEntity> login(@RequestBody LoginRequest loginRequest) { + String token = authenticate.login(loginRequest); + return ResponseEntity.ok(token); + } +} + diff --git a/src/main/java/asia/yulinling/workflow/controller/LoginController.java b/src/main/java/asia/yulinling/workflow/controller/LoginController.java deleted file mode 100644 index e6c9cad..0000000 --- a/src/main/java/asia/yulinling/workflow/controller/LoginController.java +++ /dev/null @@ -1,51 +0,0 @@ -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; - -/** - *- * 登录控制层 - *
- * - * @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("用户名或密码错误"); - } - } -} - diff --git a/src/main/java/asia/yulinling/workflow/dto/response/JWTAuthResponse.java b/src/main/java/asia/yulinling/workflow/dto/response/JWTAuthResponse.java new file mode 100644 index 0000000..9fc606f --- /dev/null +++ b/src/main/java/asia/yulinling/workflow/dto/response/JWTAuthResponse.java @@ -0,0 +1,21 @@ +package asia.yulinling.workflow.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *+ * JWT响应体 + *
+ * + * @author YLL + * @since 2025/6/15 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class JWTAuthResponse { + private String accessToken; + private String tokenType = "Bearer"; +} diff --git a/src/main/java/asia/yulinling/workflow/security/AuthEntryPoint.java b/src/main/java/asia/yulinling/workflow/security/JwtAuthenticationEntryPoint.java similarity index 90% rename from src/main/java/asia/yulinling/workflow/security/AuthEntryPoint.java rename to src/main/java/asia/yulinling/workflow/security/JwtAuthenticationEntryPoint.java index cb72d43..5e70813 100644 --- a/src/main/java/asia/yulinling/workflow/security/AuthEntryPoint.java +++ b/src/main/java/asia/yulinling/workflow/security/JwtAuthenticationEntryPoint.java @@ -17,7 +17,7 @@ import java.io.IOException; * @since 2025/6/13 */ @Component -public class AuthEntryPoint implements AuthenticationEntryPoint { +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { diff --git a/src/main/java/asia/yulinling/workflow/security/JwtAuthenticationFilter.java b/src/main/java/asia/yulinling/workflow/security/JwtAuthenticationFilter.java index 88e4bd4..c406037 100644 --- a/src/main/java/asia/yulinling/workflow/security/JwtAuthenticationFilter.java +++ b/src/main/java/asia/yulinling/workflow/security/JwtAuthenticationFilter.java @@ -7,11 +7,12 @@ 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.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; @@ -28,39 +29,40 @@ import java.io.IOException; @Slf4j public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtUtil jwtUtil; - private final AuthenticationEntryPoint authenticationEntryPoint; - private final RequestMatcher requestMatcher; + private final UserDetailsService userDetailsService; @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 filterChain) throws ServletException, IOException { + + String token = getTokenFromRequest(request); + log.info("token: {}", token); + + if (StringUtils.hasText(token) && jwtUtil.validateToken(token)) { + // get username from token + String username = jwtUtil.parseToken(token).getSubject(); + + // load the user associated with token + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + + UsernamePasswordAuthenticationToken authenticationToken + = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + SecurityContextHolder.getContext().setAuthentication(authenticationToken); } filterChain.doFilter(request, response); } + + private String getTokenFromRequest(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (bearerToken != null && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); + } + + return null; + } } diff --git a/src/main/java/asia/yulinling/workflow/security/JwtAuthenticationToken.java b/src/main/java/asia/yulinling/workflow/security/JwtAuthenticationToken.java deleted file mode 100644 index 62077e0..0000000 --- a/src/main/java/asia/yulinling/workflow/security/JwtAuthenticationToken.java +++ /dev/null @@ -1,37 +0,0 @@ -package asia.yulinling.workflow.security; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.authority.SimpleGrantedAuthority; - -import java.util.Collections; - -/** - *- * JWT认证 - *
- * - * @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 ""; - } -} diff --git a/src/main/java/asia/yulinling/workflow/service/CustomerDetailsService.java b/src/main/java/asia/yulinling/workflow/security/JwtUserDetailsService.java similarity index 70% rename from src/main/java/asia/yulinling/workflow/service/CustomerDetailsService.java rename to src/main/java/asia/yulinling/workflow/security/JwtUserDetailsService.java index 24d979e..a9fee01 100644 --- a/src/main/java/asia/yulinling/workflow/service/CustomerDetailsService.java +++ b/src/main/java/asia/yulinling/workflow/security/JwtUserDetailsService.java @@ -1,16 +1,15 @@ -package asia.yulinling.workflow.service; +package asia.yulinling.workflow.security; -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.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -18,6 +17,7 @@ import org.springframework.stereotype.Service; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; /** @@ -31,10 +31,9 @@ import java.util.stream.Collectors; @Service @RequiredArgsConstructor @Slf4j -public class CustomerDetailsService implements UserDetailsService { +public class JwtUserDetailsService implements UserDetailsService { private final UserMapper userMapper; private final RoleMapper roleMapper; - private final PermissionMapper permissionMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { @@ -47,9 +46,15 @@ public class CustomerDetailsService implements UserDetailsService { User user = userMapper.selectOne(queryWrapper); List+ * 账号权限类 + *
+ * + * @author YLL + * @since 2025/6/14 + */ +public interface AuthService { + String login(LoginRequest loginRequest); +} diff --git a/src/main/java/asia/yulinling/workflow/service/impl/AuthServiceImpl.java b/src/main/java/asia/yulinling/workflow/service/impl/AuthServiceImpl.java new file mode 100644 index 0000000..d106128 --- /dev/null +++ b/src/main/java/asia/yulinling/workflow/service/impl/AuthServiceImpl.java @@ -0,0 +1,36 @@ +package asia.yulinling.workflow.service.impl; + +import asia.yulinling.workflow.dto.request.LoginRequest; +import asia.yulinling.workflow.service.AuthService; +import asia.yulinling.workflow.utils.JwtUtil; +import lombok.RequiredArgsConstructor; +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.stereotype.Service; + +/** + *+ * 账号权限类实现 + *
+ * + * @author YLL + * @since 2025/6/14 + */ +@Service +@RequiredArgsConstructor +public class AuthServiceImpl implements AuthService { + + private final AuthenticationManager authenticationManager; + private final JwtUtil jwtUtil; + + @Override + public String login(LoginRequest loginRequest) { + Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( + loginRequest.getUsername(), loginRequest.getPassword() + )); + SecurityContextHolder.getContext().setAuthentication(authentication); + return jwtUtil.generateToken(authentication, false); + } +} diff --git a/src/main/java/asia/yulinling/workflow/utils/JwtUtil.java b/src/main/java/asia/yulinling/workflow/utils/JwtUtil.java index 59dd2ea..f8ea3c6 100644 --- a/src/main/java/asia/yulinling/workflow/utils/JwtUtil.java +++ b/src/main/java/asia/yulinling/workflow/utils/JwtUtil.java @@ -1,21 +1,16 @@ package asia.yulinling.workflow.utils; -import asia.yulinling.workflow.config.JwtConfig; -import asia.yulinling.workflow.model.vo.user.UserPrincipal; +import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import io.jsonwebtoken.*; import org.springframework.stereotype.Component; -import java.nio.charset.StandardCharsets; import java.security.Key; -import java.util.Collection; import java.util.Date; -import java.util.List; /** *
@@ -25,47 +20,27 @@ import java.util.List;
* @author YLL
* @since 2025/6/11
*/
-@EnableConfigurationProperties(JwtConfig.class)
@RequiredArgsConstructor
@Component
@Slf4j
public class JwtUtil {
- private final JwtConfig jwtConfig;
+ /**
+ * jwt 加密 key,默认值:kw.
+ */
+ @Value("${jwt.config.key}")
+ private String key = "daf66e01593f61a15b857cf433aae03a005812b31234e149036bcc8dee755dbb";
/**
- * 创建JWT
- *
- * @param isRememberMe 记住我
- * @param userId 用户id
- * @param subject 用户名
- * @param roles 用户角色
- * @param authorities 用户权限
- * @return JWT
+ * jwt 过期时间,默认值:600000 {@code 10 分钟}.
*/
- public String generateToken(boolean isRememberMe, Long userId, String subject, List