[admin] 로그인 로그아웃 작업 추가
This commit is contained in:
@@ -14,9 +14,13 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
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.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@MapperScan(
|
@MapperScan(
|
||||||
basePackages = "com.alist.api.modules",
|
basePackages = "com.alist.api.modules",
|
||||||
@@ -51,10 +55,18 @@ public class MainDataSourceConfig {
|
|||||||
factoryBean.setDataSource(mainDataSource);
|
factoryBean.setDataSource(mainDataSource);
|
||||||
factoryBean.setTypeAliasesPackage("com.alist.api");
|
factoryBean.setTypeAliasesPackage("com.alist.api");
|
||||||
factoryBean.setConfiguration(mybatisConfiguration());
|
factoryBean.setConfiguration(mybatisConfiguration());
|
||||||
factoryBean.setMapperLocations(
|
|
||||||
new PathMatchingResourcePatternResolver()
|
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||||
.getResources("classpath:mapper/*/*.xml")
|
|
||||||
);
|
Resource[] commonMappers = resolver.getResources("classpath:mapper/*/*.xml");
|
||||||
|
Resource[] adminMappers = resolver.getResources("classpath:mapper/admin/**/*.xml");
|
||||||
|
|
||||||
|
Resource[] mapperLocations = Stream.concat(
|
||||||
|
Arrays.stream(commonMappers),
|
||||||
|
Arrays.stream(adminMappers)
|
||||||
|
).toArray(Resource[]::new);
|
||||||
|
|
||||||
|
factoryBean.setMapperLocations(mapperLocations);
|
||||||
return factoryBean.getObject();
|
return factoryBean.getObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,25 @@ public class SecurityConfig {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Order(2)
|
@Order(2)
|
||||||
|
public SecurityFilterChain adminFilterChain(
|
||||||
|
HttpSecurity http,
|
||||||
|
JwtAuthenticationFilter jwtAuthenticationFilter
|
||||||
|
) throws Exception {
|
||||||
|
http
|
||||||
|
.securityMatcher("/admin/**")
|
||||||
|
.cors(Customizer.withDefaults())
|
||||||
|
.csrf(csrf -> csrf.disable())
|
||||||
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
.requestMatchers("/admin/auth/**").permitAll()
|
||||||
|
.anyRequest().hasRole("ADMIN")
|
||||||
|
)
|
||||||
|
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(3)
|
||||||
public SecurityFilterChain apiFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
|
public SecurityFilterChain apiFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
|
||||||
http
|
http
|
||||||
.cors(Customizer.withDefaults())
|
.cors(Customizer.withDefaults())
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.List;
|
||||||
|
|
||||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
@@ -25,25 +26,50 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String token = resolveToken(request);
|
String requestUri = request.getRequestURI();
|
||||||
|
boolean adminRequest = requestUri.startsWith("/admin/");
|
||||||
|
String token = resolveToken(request, adminRequest);
|
||||||
|
|
||||||
if (token != null && jwtTokenProvider.validateToken(token)) {
|
if (token != null && jwtTokenProvider.validateToken(token)) {
|
||||||
String userId = jwtTokenProvider.getUserTokenIdx(token);
|
String principal = jwtTokenProvider.getUserTokenIdx(token);
|
||||||
|
String role = jwtTokenProvider.getRole(token);
|
||||||
|
String scope = jwtTokenProvider.getScope(token);
|
||||||
|
String tokenType = jwtTokenProvider.getTokenType(token);
|
||||||
|
|
||||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userId, null, Collections.emptyList());
|
if (adminRequest) {
|
||||||
|
if (!"ADMIN".equals(scope) || !"ACCESS".equals(tokenType) || !"ADMIN".equals(role)) {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Admin token required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ("ADMIN".equals(scope) || "UPLOAD".equals(scope) || !"ACCESS".equals(tokenType)) {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access token required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UsernamePasswordAuthenticationToken authentication =
|
||||||
|
new UsernamePasswordAuthenticationToken(
|
||||||
|
principal,
|
||||||
|
null,
|
||||||
|
role == null
|
||||||
|
? List.of()
|
||||||
|
: List.of(new SimpleGrantedAuthority("ROLE_" + role))
|
||||||
|
);
|
||||||
|
|
||||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
SecurityContextHolder.clearContext();
|
SecurityContextHolder.clearContext();
|
||||||
// 로깅은 여기서 해도 됨 (SLF4J)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveToken(HttpServletRequest request) {
|
private String resolveToken(HttpServletRequest request, boolean adminRequest) {
|
||||||
// 1) Authorization: Bearer xxx 우선
|
// 1) Authorization: Bearer xxx 우선
|
||||||
String bearer = request.getHeader(HttpHeaders.AUTHORIZATION);
|
String bearer = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||||
if (bearer != null && bearer.startsWith("Bearer ")) {
|
if (bearer != null && bearer.startsWith("Bearer ")) {
|
||||||
@@ -55,16 +81,20 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
// 2) 쿠키 fallback (브라우저용)
|
// 2) 쿠키 fallback (브라우저용)
|
||||||
Cookie[] cookies = request.getCookies();
|
Cookie[] cookies = request.getCookies();
|
||||||
if (cookies != null) {
|
if (cookies == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String cookieName = adminRequest ? "adminAccessToken" : "accessToken";
|
||||||
|
|
||||||
for (Cookie cookie : cookies) {
|
for (Cookie cookie : cookies) {
|
||||||
if ("accessToken".equals(cookie.getName())) {
|
if (cookieName.equals(cookie.getName())) {
|
||||||
String token = cookie.getValue();
|
String token = cookie.getValue();
|
||||||
if (token != null && !token.isBlank()) {
|
if (token != null && !token.isBlank()) {
|
||||||
return token.trim();
|
return token.trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class JwtTokenProvider {
|
|||||||
|
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.setSubject(userId)
|
.setSubject(userId)
|
||||||
|
.claim("tokenType", "ACCESS")
|
||||||
.setIssuedAt(now)
|
.setIssuedAt(now)
|
||||||
.setExpiration(expiry)
|
.setExpiration(expiry)
|
||||||
.signWith(secretKey, SignatureAlgorithm.HS256)
|
.signWith(secretKey, SignatureAlgorithm.HS256)
|
||||||
@@ -48,6 +49,7 @@ public class JwtTokenProvider {
|
|||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.setSubject(String.valueOf(userTokenIdx))
|
.setSubject(String.valueOf(userTokenIdx))
|
||||||
.claim("role", role)
|
.claim("role", role)
|
||||||
|
.claim("tokenType", "ACCESS")
|
||||||
.setIssuedAt(Date.from(now))
|
.setIssuedAt(Date.from(now))
|
||||||
.setExpiration(Date.from(expiry))
|
.setExpiration(Date.from(expiry))
|
||||||
.signWith(secretKey, SignatureAlgorithm.HS256)
|
.signWith(secretKey, SignatureAlgorithm.HS256)
|
||||||
@@ -61,6 +63,7 @@ public class JwtTokenProvider {
|
|||||||
|
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.setSubject(String.valueOf(userTokenIdx))
|
.setSubject(String.valueOf(userTokenIdx))
|
||||||
|
.claim("tokenType", "REFRESH")
|
||||||
.setIssuedAt(Date.from(now))
|
.setIssuedAt(Date.from(now))
|
||||||
.setExpiration(Date.from(expiry))
|
.setExpiration(Date.from(expiry))
|
||||||
.signWith(secretKey, SignatureAlgorithm.HS256)
|
.signWith(secretKey, SignatureAlgorithm.HS256)
|
||||||
@@ -90,7 +93,6 @@ public class JwtTokenProvider {
|
|||||||
.getBody();
|
.getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 업로드 전용 토큰*/
|
/* 업로드 전용 토큰*/
|
||||||
public String createUploadToken(long userTokenIdx) {
|
public String createUploadToken(long userTokenIdx) {
|
||||||
Instant now = Instant.now();
|
Instant now = Instant.now();
|
||||||
@@ -114,4 +116,49 @@ public class JwtTokenProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String createAdminAccessToken(long userTokenIdx) {
|
||||||
|
Instant now = Instant.now();
|
||||||
|
Instant expiry = now.plusSeconds(jwtProperties.getAccessTokenValiditySeconds());
|
||||||
|
|
||||||
|
return Jwts.builder()
|
||||||
|
.setSubject(String.valueOf(userTokenIdx))
|
||||||
|
.claim("role", "ADMIN")
|
||||||
|
.claim("scope", "ADMIN")
|
||||||
|
.claim("tokenType", "ACCESS")
|
||||||
|
.setIssuedAt(Date.from(now))
|
||||||
|
.setExpiration(Date.from(expiry))
|
||||||
|
.signWith(secretKey, SignatureAlgorithm.HS256)
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createAdminRefreshToken(long userTokenIdx) {
|
||||||
|
Instant now = Instant.now();
|
||||||
|
Instant expiry = now.plusSeconds(jwtProperties.getRefreshTokenValiditySeconds());
|
||||||
|
|
||||||
|
return Jwts.builder()
|
||||||
|
.setSubject(String.valueOf(userTokenIdx))
|
||||||
|
.claim("role", "ADMIN")
|
||||||
|
.claim("scope", "ADMIN")
|
||||||
|
.claim("tokenType", "REFRESH")
|
||||||
|
.setIssuedAt(Date.from(now))
|
||||||
|
.setExpiration(Date.from(expiry))
|
||||||
|
.signWith(secretKey, SignatureAlgorithm.HS256)
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Claims getClaims(String token) {
|
||||||
|
return parseClaims(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRole(String token) {
|
||||||
|
return parseClaims(token).get("role", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScope(String token) {
|
||||||
|
return parseClaims(token).get("scope", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTokenType(String token) {
|
||||||
|
return parseClaims(token).get("tokenType", String.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.alist.api.modules.admin.auth;
|
||||||
|
|
||||||
|
import com.alist.api.common.response.ApiResponse;
|
||||||
|
import com.alist.api.common.response.ApiResponseCode;
|
||||||
|
import com.alist.api.modules.admin.auth.form.AdminLoginForm;
|
||||||
|
import com.alist.api.modules.admin.auth.service.AdminAuthService;
|
||||||
|
import com.alist.api.modules.admin.auth.vo.AdminLoginVo;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/admin/auth")
|
||||||
|
public class AdminAuthController {
|
||||||
|
|
||||||
|
private final AdminAuthService adminAuthService;
|
||||||
|
|
||||||
|
public AdminAuthController(AdminAuthService adminAuthService) {
|
||||||
|
this.adminAuthService = adminAuthService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/login")
|
||||||
|
public ResponseEntity<ApiResponse<AdminLoginVo>> adminLogin(
|
||||||
|
@Valid @RequestBody AdminLoginForm adminLoginForm
|
||||||
|
, HttpServletResponse response
|
||||||
|
) {
|
||||||
|
AdminLoginVo result = adminAuthService.adminLogin(adminLoginForm.toAdminLoginDto(), response);
|
||||||
|
|
||||||
|
if (result.getResultCode() == 2003) {
|
||||||
|
return ApiResponse.entity(result, ApiResponseCode.CODE_2003);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse.entity(result, ApiResponseCode.CODE_2001, "관리자 로그인");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/refresh")
|
||||||
|
public ResponseEntity<ApiResponse<Map<String, Object>>> refresh(
|
||||||
|
@CookieValue(name = "adminRefreshToken", required = false) String refreshToken,
|
||||||
|
HttpServletResponse response
|
||||||
|
) {
|
||||||
|
AdminLoginVo result = adminAuthService.adminRefresh(refreshToken, response);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
return ApiResponse.entity(Map.of("refreshed", false), ApiResponseCode.CODE_401);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse.entity(Map.of("refreshed", true), ApiResponseCode.CODE_200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public ResponseEntity<ApiResponse<Map<String, Object>>> logout(
|
||||||
|
@CookieValue(name = "adminRefreshToken", required = false) String refreshToken,
|
||||||
|
HttpServletResponse response
|
||||||
|
) {
|
||||||
|
adminAuthService.adminLogout(refreshToken, response);
|
||||||
|
|
||||||
|
return ApiResponse.entity(Map.of("loggedOut", true), ApiResponseCode.CODE_200);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.alist.api.modules.admin.auth.dto;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
public class AdminLoginDto {
|
||||||
|
private String id;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
private Integer userIdx;
|
||||||
|
private Integer userTokenIdx;
|
||||||
|
private String refreshToken;
|
||||||
|
private Instant expiresAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.alist.api.modules.admin.auth.form;
|
||||||
|
|
||||||
|
import com.alist.api.modules.admin.auth.dto.AdminLoginDto;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class AdminLoginForm {
|
||||||
|
@NotBlank
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public AdminLoginDto toAdminLoginDto() {
|
||||||
|
AdminLoginDto adminLoginDto = new AdminLoginDto();
|
||||||
|
adminLoginDto.setId(id.trim());
|
||||||
|
adminLoginDto.setPassword(password);
|
||||||
|
|
||||||
|
return adminLoginDto;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.alist.api.modules.admin.auth.mapper;
|
||||||
|
|
||||||
|
import com.alist.api.modules.admin.auth.dto.AdminLoginDto;
|
||||||
|
import com.alist.api.modules.admin.auth.vo.AdminLoginVo;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface AdminAuthMapper {
|
||||||
|
AdminLoginVo selectAdminLoginById(String id);
|
||||||
|
|
||||||
|
void updateAdminRefreshToken(AdminLoginDto adminLoginDto);
|
||||||
|
|
||||||
|
void updateAdminLastLoginAt(Integer userIdx);
|
||||||
|
|
||||||
|
AdminLoginVo selectAdminTokenByUserTokenIdx(Integer userTokenIdx);
|
||||||
|
|
||||||
|
void clearAdminRefreshToken(Integer userTokenIdx);
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
package com.alist.api.modules.admin.auth.service;
|
||||||
|
|
||||||
|
import com.alist.api.common.utils.SessionUtil;
|
||||||
|
import com.alist.api.config.jwt.JwtTokenProvider;
|
||||||
|
import com.alist.api.modules.admin.auth.dto.AdminLoginDto;
|
||||||
|
import com.alist.api.modules.admin.auth.mapper.AdminAuthMapper;
|
||||||
|
import com.alist.api.modules.admin.auth.vo.AdminLoginVo;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AdminAuthService {
|
||||||
|
@Value("${cookie.secure}")
|
||||||
|
private boolean cookieSecure;
|
||||||
|
|
||||||
|
@Value("${cookie.domain:}")
|
||||||
|
private String cookieDomain;
|
||||||
|
|
||||||
|
@Value("${cookie.same-site:Lax}")
|
||||||
|
private String cookieSameSite;
|
||||||
|
|
||||||
|
@Value("${jwt.access-token-validity-seconds}")
|
||||||
|
private long accessTokenValiditySeconds;
|
||||||
|
|
||||||
|
@Value("${jwt.refresh-token-validity-seconds}")
|
||||||
|
private long refreshTokenValiditySeconds;
|
||||||
|
|
||||||
|
private final AdminAuthMapper adminAuthMapper;
|
||||||
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
public AdminAuthService(AdminAuthMapper adminAuthMapper, JwtTokenProvider jwtTokenProvider, PasswordEncoder passwordEncoder) {
|
||||||
|
this.adminAuthMapper = adminAuthMapper;
|
||||||
|
this.jwtTokenProvider = jwtTokenProvider;
|
||||||
|
this.passwordEncoder = passwordEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public AdminLoginVo adminLogin(AdminLoginDto adminLoginDto, HttpServletResponse response) {
|
||||||
|
AdminLoginVo result = new AdminLoginVo();
|
||||||
|
AdminLoginVo adminLoginVo = adminAuthMapper.selectAdminLoginById(adminLoginDto.getId());
|
||||||
|
|
||||||
|
if (adminLoginVo == null) {
|
||||||
|
result.setResultCode(2003);
|
||||||
|
result.setLogin(false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!passwordEncoder.matches(adminLoginDto.getPassword(), adminLoginVo.getPassword())) {
|
||||||
|
result.setResultCode(2003);
|
||||||
|
result.setLogin(false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
String accessToken = jwtTokenProvider.createAdminAccessToken(adminLoginVo.getUserTokenIdx());
|
||||||
|
String refreshToken = jwtTokenProvider.createAdminRefreshToken(adminLoginVo.getUserTokenIdx());
|
||||||
|
|
||||||
|
adminLoginDto.setUserIdx(adminLoginVo.getUserIdx());
|
||||||
|
adminLoginDto.setUserTokenIdx(adminLoginVo.getUserTokenIdx());
|
||||||
|
adminLoginDto.setRefreshToken(refreshToken);
|
||||||
|
adminLoginDto.setExpiresAt(Instant.now().plusSeconds(refreshTokenValiditySeconds));
|
||||||
|
|
||||||
|
adminAuthMapper.updateAdminRefreshToken(adminLoginDto);
|
||||||
|
adminAuthMapper.updateAdminLastLoginAt(adminLoginDto.getUserIdx());
|
||||||
|
|
||||||
|
SessionUtil.addTokenCookie(response,"adminAccessToken", accessToken, cookieDomain, cookieSecure, cookieSameSite, accessTokenValiditySeconds);
|
||||||
|
SessionUtil.addTokenCookie(response,"adminRefreshToken", refreshToken, cookieDomain, cookieSecure, cookieSameSite, refreshTokenValiditySeconds);
|
||||||
|
|
||||||
|
adminLoginVo.setResultCode(2001);
|
||||||
|
adminLoginVo.setLogin(true);
|
||||||
|
return adminLoginVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AdminLoginVo adminRefresh(String refreshToken, HttpServletResponse response) {
|
||||||
|
if (refreshToken == null || !jwtTokenProvider.validateToken(refreshToken)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String role = jwtTokenProvider.getRole(refreshToken);
|
||||||
|
String scope = jwtTokenProvider.getScope(refreshToken);
|
||||||
|
String tokenType = jwtTokenProvider.getTokenType(refreshToken);
|
||||||
|
|
||||||
|
if (!"ADMIN".equals(role) || !"ADMIN".equals(scope) || !"REFRESH".equals(tokenType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer userTokenIdx;
|
||||||
|
|
||||||
|
try {
|
||||||
|
userTokenIdx = Integer.parseInt(jwtTokenProvider.getUserTokenIdx(refreshToken));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdminLoginVo adminToken = adminAuthMapper.selectAdminTokenByUserTokenIdx(userTokenIdx);
|
||||||
|
if (adminToken == null || adminToken.getRefreshToken() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!refreshToken.equals(adminToken.getRefreshToken())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String newAccessToken = jwtTokenProvider.createAdminAccessToken(userTokenIdx);
|
||||||
|
String newRefreshToken = jwtTokenProvider.createAdminRefreshToken(userTokenIdx);
|
||||||
|
|
||||||
|
AdminLoginDto adminLoginDto = new AdminLoginDto();
|
||||||
|
|
||||||
|
adminLoginDto.setUserTokenIdx(userTokenIdx);
|
||||||
|
adminLoginDto.setRefreshToken(newRefreshToken);
|
||||||
|
adminLoginDto.setExpiresAt(Instant.now().plusSeconds(refreshTokenValiditySeconds));
|
||||||
|
|
||||||
|
adminAuthMapper.updateAdminRefreshToken(adminLoginDto);
|
||||||
|
|
||||||
|
SessionUtil.addTokenCookie(response, "adminAccessToken", newAccessToken, cookieDomain, cookieSecure, cookieSameSite, accessTokenValiditySeconds);
|
||||||
|
SessionUtil.addTokenCookie(response, "adminRefreshToken", newRefreshToken, cookieDomain, cookieSecure, cookieSameSite, refreshTokenValiditySeconds);
|
||||||
|
|
||||||
|
adminToken.setResultCode(2001);
|
||||||
|
return adminToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void adminLogout(String refreshToken, HttpServletResponse response) {
|
||||||
|
clearAdminRefreshToken(refreshToken);
|
||||||
|
|
||||||
|
SessionUtil.expireCookie(response, "adminAccessToken", cookieDomain, cookieSecure, cookieSameSite);
|
||||||
|
SessionUtil.expireCookie(response, "adminRefreshToken", cookieDomain, cookieSecure, cookieSameSite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearAdminRefreshToken(String refreshToken) {
|
||||||
|
if (refreshToken == null || !jwtTokenProvider.validateToken(refreshToken)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String role = jwtTokenProvider.getRole(refreshToken);
|
||||||
|
String scope = jwtTokenProvider.getScope(refreshToken);
|
||||||
|
String tokenType = jwtTokenProvider.getTokenType(refreshToken);
|
||||||
|
|
||||||
|
if (!"ADMIN".equals(role) || !"ADMIN".equals(scope) || !"REFRESH".equals(tokenType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Integer userTokenIdx = Integer.parseInt(jwtTokenProvider.getUserTokenIdx(refreshToken));
|
||||||
|
adminAuthMapper.clearAdminRefreshToken(userTokenIdx);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.alist.api.modules.admin.auth.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
public class AdminLoginVo {
|
||||||
|
private boolean isLogin;
|
||||||
|
private Integer userIdx;
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Integer userTokenIdx;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private String refreshToken;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private int resultCode;
|
||||||
|
}
|
||||||
@@ -46,7 +46,7 @@ jwt:
|
|||||||
|
|
||||||
cookie:
|
cookie:
|
||||||
secure: false # (HTTP) -> false
|
secure: false # (HTTP) -> false
|
||||||
domain: api-alist.pjt.kr # (HTTP) -> 비워두세요
|
domain: # (HTTP) -> 비워두세요
|
||||||
name: ALIST_SSO
|
name: ALIST_SSO
|
||||||
same-site: Lax # (HTTP) -> Lax
|
same-site: Lax # (HTTP) -> Lax
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
|
||||||
|
<mapper namespace="com.alist.api.modules.admin.auth.mapper.AdminAuthMapper">
|
||||||
|
<update id="updateAdminRefreshToken">
|
||||||
|
/*AdminLoginMapper.updateAdminRefreshToken*/
|
||||||
|
UPDATE ALISTLMS.test_user_token
|
||||||
|
SET refresh_token = #{refreshToken}
|
||||||
|
, expires_at = #{expiresAt}
|
||||||
|
, updated_at = now()
|
||||||
|
WHERE user_token_idx = #{userTokenIdx}
|
||||||
|
AND user_role = 'admin'
|
||||||
|
</update>
|
||||||
|
<update id="updateAdminLastLoginAt">
|
||||||
|
/*AdminLoginMapper.updateAdminLastLoginAt*/
|
||||||
|
UPDATE ALISTLMS.test_user
|
||||||
|
SET last_login_at = now()
|
||||||
|
, update_at = now()
|
||||||
|
WHERE user_idx = #{userIdx}
|
||||||
|
</update>
|
||||||
|
<update id="clearAdminRefreshToken">
|
||||||
|
/*AdminLoginMapper.clearAdminRefreshToken*/
|
||||||
|
UPDATE ALISTLMS.test_user_token
|
||||||
|
SET refresh_token = null
|
||||||
|
, expires_at = null
|
||||||
|
, updated_at = now()
|
||||||
|
WHERE user_token_idx = #{userTokenIdx}
|
||||||
|
AND user_role = 'admin'
|
||||||
|
</update>
|
||||||
|
<select id="selectAdminLoginById" resultType="com.alist.api.modules.admin.auth.vo.AdminLoginVo">
|
||||||
|
/*AdminLoginMapper.selectAdminLoginById*/
|
||||||
|
SELECT ATU.user_idx
|
||||||
|
, ATU.id
|
||||||
|
, ATU.password
|
||||||
|
, ATUT.user_token_idx
|
||||||
|
FROM ALISTLMS.test_user ATU
|
||||||
|
INNER JOIN ALISTLMS.test_user_token ATUT ON ATUT.user_idx = ATU.user_idx
|
||||||
|
WHERE ATU.id = #{id}
|
||||||
|
AND ATU.del_yn = 1
|
||||||
|
AND ATUT.user_role = 'admin'
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
<select id="selectAdminTokenByUserTokenIdx" resultType="com.alist.api.modules.admin.auth.vo.AdminLoginVo">
|
||||||
|
/*AdminLoginMapper.selectAdminTokenByUserTokenIdx*/
|
||||||
|
SELECT ATU.user_idx
|
||||||
|
, ATU.id
|
||||||
|
, ATUT.user_token_idx
|
||||||
|
, ATUT.refresh_token
|
||||||
|
FROM ALISTLMS.test_user_token ATUT
|
||||||
|
INNER JOIN ALISTLMS.test_user ATU ON ATU.user_idx = ATUT.user_idx
|
||||||
|
WHERE ATUT.user_token_idx = #{userTokenIdx}
|
||||||
|
AND ATUT.user_role = 'admin'
|
||||||
|
AND ATU.del_yn = 1
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
Reference in New Issue
Block a user