From f0582a11ab55d4fa59635b20c15be6862f0a51a7 Mon Sep 17 00:00:00 2001 From: xiang Date: Sun, 31 Aug 2025 00:21:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=9D=83=E9=99=90v1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiang/xservice/auth/api/api/TokenApi.java | 15 ++++++++++++ .../auth/api/code/Code02RoleErrorCode.java | 1 + .../auth/api/dto/req/LoginRequest.java | 13 ++++++++++- .../server/controller/TokenController.java | 3 ++- .../server/controller/UserController.java | 2 +- .../config/AuthorizationServerConfig.java | 23 ++++++++++++++++++- .../repository/mapper/XUserRoleMapper.java | 2 ++ .../service/CustomUserDetailsService.java | 20 ++++++++++++++-- .../service/impl/XUserServiceImpl.java | 22 ++++++++++++++++-- .../resources/mapper/user/XUserRoleMapper.xml | 5 ++++ 10 files changed, 98 insertions(+), 8 deletions(-) diff --git a/xs-api/src/main/java/com/xiang/xservice/auth/api/api/TokenApi.java b/xs-api/src/main/java/com/xiang/xservice/auth/api/api/TokenApi.java index 191eb45..e416db3 100644 --- a/xs-api/src/main/java/com/xiang/xservice/auth/api/api/TokenApi.java +++ b/xs-api/src/main/java/com/xiang/xservice/auth/api/api/TokenApi.java @@ -1,4 +1,19 @@ package com.xiang.xservice.auth.api.api; + +import com.xiang.xservice.auth.api.dto.req.LoginRequest; +import com.xiang.xservice.auth.api.dto.req.RegisterRequest; +import com.xiang.xservice.auth.api.dto.resp.LoginResp; +import com.xiang.xservice.auth.api.dto.resp.RegisterResp; +import com.xiang.xservice.basic.common.resp.Result; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + public interface TokenApi { + + Result login(@RequestBody @NotNull(message = "请求参数不能为空") @Valid LoginRequest request); + + Result register(@RequestBody @Valid @NotNull(message = "请求参数不能为空") RegisterRequest request); } diff --git a/xs-api/src/main/java/com/xiang/xservice/auth/api/code/Code02RoleErrorCode.java b/xs-api/src/main/java/com/xiang/xservice/auth/api/code/Code02RoleErrorCode.java index b6561a5..652f3ac 100644 --- a/xs-api/src/main/java/com/xiang/xservice/auth/api/code/Code02RoleErrorCode.java +++ b/xs-api/src/main/java/com/xiang/xservice/auth/api/code/Code02RoleErrorCode.java @@ -13,6 +13,7 @@ import lombok.Getter; public enum Code02RoleErrorCode implements BaseErrorCode { ROLE_NOT_EXISTS("A1000201", "角色不存在"), + USER_ROLE_NOT_EXISTS("A1000202", "用户角色权限不存在!") ; private final String code; diff --git a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/req/LoginRequest.java b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/req/LoginRequest.java index 6b6f501..d837dba 100644 --- a/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/req/LoginRequest.java +++ b/xs-api/src/main/java/com/xiang/xservice/auth/api/dto/req/LoginRequest.java @@ -5,12 +5,23 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import javax.validation.constraints.NotBlank; + @Data @AllArgsConstructor @NoArgsConstructor public class LoginRequest { + @NotBlank(message = "用户名不能为空") private String username; - + @NotBlank(message = "密码不能为空") private String password; + + /** + * todo 目前只支持用户名密码登录 + * 1. 用户名密码登录 + * 2. 手机号密码登录 + * 3. 邮箱密码登录 + */ + private Integer loginType; } diff --git a/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/TokenController.java b/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/TokenController.java index 49443cc..56f019d 100644 --- a/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/TokenController.java +++ b/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/TokenController.java @@ -1,5 +1,6 @@ package com.xiang.xservice.auth.server.controller; +import com.xiang.xservice.auth.api.api.TokenApi; import com.xiang.xservice.auth.api.dto.req.LoginRequest; import com.xiang.xservice.auth.api.dto.req.RegisterRequest; import com.xiang.xservice.auth.api.dto.resp.LoginResp; @@ -20,7 +21,7 @@ import java.util.Objects; @Slf4j @RestController @RequiredArgsConstructor -public class TokenController { +public class TokenController implements TokenApi { private final XUserService userService; diff --git a/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/UserController.java b/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/UserController.java index 32e0ea3..97ec5c7 100644 --- a/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/UserController.java +++ b/xs-server/src/main/java/com/xiang/xservice/auth/server/controller/UserController.java @@ -12,6 +12,7 @@ import com.xiang.xservice.basic.common.resp.Result; import com.xiang.xservice.basic.exception.BusinessException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -31,7 +32,6 @@ public class UserController { private final XUserService userService; - @PostMapping("/private/user/list") public Result getUserList(@RequestBody @Valid @NotNull(message = "请求参数不能为空") UserQueryRequest request) { return Result.success(userService.getUserList(request)); diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/config/AuthorizationServerConfig.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/config/AuthorizationServerConfig.java index bda77aa..d471bf0 100644 --- a/xs-service/src/main/java/com/xiang/xservice/auth/service/config/AuthorizationServerConfig.java +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/config/AuthorizationServerConfig.java @@ -4,6 +4,8 @@ import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.source.JWKSource; import com.nimbusds.jose.proc.SecurityContext; +import com.xiang.xservice.basic.exception.CustomAccessDeniedHandler; +import com.xiang.xservice.basic.exception.CustomAuthenticationEntryPoint; import com.xiang.xservice.basic.utils.JwkUtils; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; @@ -21,6 +23,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; +import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtEncoder; import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository; @@ -45,6 +48,8 @@ public class AuthorizationServerConfig { @Value("${user.auth.redirectUrl}") private String redirectUrl; private final JdbcTemplate jdbcTemplate; + private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint; + private final CustomAccessDeniedHandler customAccessDeniedHandler; @Bean @Order(1) @@ -62,9 +67,20 @@ public class AuthorizationServerConfig { .authorizeRequests(authorizeRequests -> authorizeRequests .antMatchers("/public/**").permitAll() .antMatchers("/open/**").permitAll() + .antMatchers("/private/**").authenticated() .anyRequest().authenticated() ) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + .exceptionHandling(exception -> + exception + .authenticationEntryPoint(customAuthenticationEntryPoint) + .accessDeniedHandler(customAccessDeniedHandler)) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .oauth2ResourceServer(oauth -> + oauth + .jwt() + .and() + .authenticationEntryPoint(customAuthenticationEntryPoint) + .accessDeniedHandler(customAccessDeniedHandler)); return http.build(); } @@ -131,5 +147,10 @@ public class AuthorizationServerConfig { .issuer(issuer) .build(); } + + @Bean + public JwtDecoder jwtDecoder(JWKSource jwkSource) { + return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource); + } } diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XUserRoleMapper.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XUserRoleMapper.java index 5a37705..75cb2e5 100644 --- a/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XUserRoleMapper.java +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/repository/mapper/XUserRoleMapper.java @@ -18,4 +18,6 @@ public interface XUserRoleMapper { int delByRoleIds(@Param("list") List roleIds); int addBatch(List list); + + List getByUserId(@Param("userId") Long userId); } diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/CustomUserDetailsService.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/CustomUserDetailsService.java index 07f87ac..942792e 100644 --- a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/CustomUserDetailsService.java +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/CustomUserDetailsService.java @@ -1,13 +1,21 @@ package com.xiang.xservice.auth.service.service; +import com.google.common.collect.Lists; +import com.xiang.xservice.auth.service.entity.XRole; import com.xiang.xservice.auth.service.entity.XUser; +import com.xiang.xservice.auth.service.entity.XUserRole; +import com.xiang.xservice.auth.service.repository.mapper.XRoleMapper; import com.xiang.xservice.auth.service.repository.mapper.XUserMapper; +import com.xiang.xservice.auth.service.repository.mapper.XUserRoleMapper; import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +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; import org.springframework.stereotype.Service; +import java.util.List; import java.util.Objects; @Service @@ -15,6 +23,8 @@ import java.util.Objects; public class CustomUserDetailsService implements UserDetailsService { private final XUserMapper userMapper; + private final XRoleMapper roleMapper; + private final XUserRoleMapper userRoleMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { @@ -22,11 +32,17 @@ public class CustomUserDetailsService implements UserDetailsService { if (Objects.isNull(user)) { throw new RuntimeException("用户不存在!"); } - + List userRoles = userRoleMapper.getByUserId(user.getId()); + List roleIds = userRoles.stream().map(XUserRole::getRoleId).toList(); + List grantedAuthorities = Lists.newArrayList(); + if (CollectionUtils.isNotEmpty(roleIds)) { + List roles = roleMapper.getRoleByIds(roleIds); + grantedAuthorities.addAll(roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role.getCode())).toList()); + } return org.springframework.security.core.userdetails.User .withUsername(user.getUsername()) .password(user.getPassword()) // 已经加密的 - .authorities("admin") + .authorities(grantedAuthorities) .build(); } } diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java index 0ee4e14..cdb12f0 100644 --- a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java +++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java @@ -2,6 +2,7 @@ package com.xiang.xservice.auth.service.service.impl; import com.google.common.collect.Lists; import com.xiang.xservice.auth.api.code.Code01UserErrorCode; +import com.xiang.xservice.auth.api.code.Code02RoleErrorCode; import com.xiang.xservice.auth.api.dto.req.LoginRequest; import com.xiang.xservice.auth.api.dto.req.RegisterRequest; import com.xiang.xservice.auth.api.dto.req.user.UserAddRequest; @@ -13,6 +14,7 @@ import com.xiang.xservice.auth.api.dto.resp.LoginResp; import com.xiang.xservice.auth.api.dto.resp.RegisterResp; import com.xiang.xservice.auth.api.dto.resp.UserResp; import com.xiang.xservice.auth.service.convert.XUserConvert; +import com.xiang.xservice.auth.service.entity.XRole; import com.xiang.xservice.auth.service.entity.XUser; import com.xiang.xservice.auth.service.entity.XUserRole; import com.xiang.xservice.auth.service.enums.UserStatusEnum; @@ -22,6 +24,7 @@ import com.xiang.xservice.auth.service.repository.mapper.XUserRoleMapper; import com.xiang.xservice.auth.service.service.XUserService; import com.xiang.xservice.basic.enums.DelStatusEnum; import com.xiang.xservice.basic.exception.BusinessException; +import com.xiang.xservice.basic.utils.JsonUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; @@ -41,6 +44,7 @@ import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; @Slf4j @Service @@ -67,16 +71,30 @@ public class XUserServiceImpl implements XUserService { throw new BusinessException(e.getMessage()); } + XUser user = userMapper.selectByUsername(request.getUsername()); + if (Objects.isNull(user)) { + throw new BusinessException(Code01UserErrorCode.USER_NOT_EXISTS); + } + List roleCodes = Lists.newArrayList(); + List userRoles = userRoleMapper.getByUserId(user.getId()); + if (CollectionUtils.isNotEmpty(userRoles)) { + List roles = roleMapper.getRoleByIds(userRoles.stream().map(XUserRole::getRoleId).collect(Collectors.toList())); + if (CollectionUtils.isEmpty(roles)) { + throw new BusinessException(Code02RoleErrorCode.ROLE_NOT_EXISTS); + } + roleCodes.addAll(roles.stream().map(XRole::getCode).toList()); + } // 生成 token Instant now = Instant.now(); - // todo 1. 构建 claims JwtClaimsSet claims = JwtClaimsSet.builder() // 对应 ProviderSettings.issuer .issuedAt(now) - .expiresAt(now.plus(1, ChronoUnit.HOURS)) + .expiresAt(now.plus(3, ChronoUnit.HOURS)) // 自定义 scope + .claim("timestamp", System.currentTimeMillis()) .claim("username", request.getUsername()) + .claim("roles", JsonUtils.toJsonString(roleCodes)) .build(); // 2. 编码生成 token diff --git a/xs-service/src/main/resources/mapper/user/XUserRoleMapper.xml b/xs-service/src/main/resources/mapper/user/XUserRoleMapper.xml index 54b606e..10f4d94 100644 --- a/xs-service/src/main/resources/mapper/user/XUserRoleMapper.xml +++ b/xs-service/src/main/resources/mapper/user/XUserRoleMapper.xml @@ -38,5 +38,10 @@ + \ No newline at end of file