package com.tourguide.security;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.tourguide.common.api.ApiResponse;
|
import com.tourguide.modules.user.domain.ClientType;
|
import com.tourguide.modules.user.domain.UserRole;
|
import com.tourguide.modules.user.entity.AdminUser;
|
import com.tourguide.modules.user.entity.GuideUser;
|
import com.tourguide.modules.user.entity.MiniUser;
|
import com.tourguide.modules.user.service.AdminUserService;
|
import com.tourguide.modules.user.service.GuideUserService;
|
import com.tourguide.modules.user.service.MiniUserService;
|
import com.tourguide.modules.system.service.AdminPermissionService;
|
import org.springframework.http.MediaType;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.stereotype.Component;
|
import org.springframework.util.StringUtils;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
|
import javax.servlet.FilterChain;
|
import javax.servlet.ServletException;
|
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletResponse;
|
import java.io.IOException;
|
import java.nio.charset.StandardCharsets;
|
|
@Component
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
private final JwtTokenService jwtTokenService;
|
private final TokenSessionService tokenSessionService;
|
private final AdminUserService adminUserService;
|
private final AdminPermissionService adminPermissionService;
|
private final MiniUserService miniUserService;
|
private final GuideUserService guideUserService;
|
private final ObjectMapper objectMapper;
|
|
public JwtAuthenticationFilter(JwtTokenService jwtTokenService, TokenSessionService tokenSessionService,
|
AdminUserService adminUserService, AdminPermissionService adminPermissionService,
|
MiniUserService miniUserService,
|
GuideUserService guideUserService, ObjectMapper objectMapper) {
|
this.jwtTokenService = jwtTokenService;
|
this.tokenSessionService = tokenSessionService;
|
this.adminUserService = adminUserService;
|
this.adminPermissionService = adminPermissionService;
|
this.miniUserService = miniUserService;
|
this.guideUserService = guideUserService;
|
this.objectMapper = objectMapper;
|
}
|
|
@Override
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
throws ServletException, IOException {
|
String token = resolveToken(request);
|
if (!StringUtils.hasText(token)) {
|
filterChain.doFilter(request, response);
|
return;
|
}
|
try {
|
TokenPayload payload = jwtTokenService.parse(token);
|
ClientType expectedClientType = resolveExpectedClientType(request.getRequestURI());
|
if (expectedClientType != null && payload.getClientType() != expectedClientType) {
|
writeUnauthorized(response, "Token端类型不匹配");
|
return;
|
}
|
if (!tokenSessionService.isActive(payload)) {
|
writeUnauthorized(response, "Token已失效");
|
return;
|
}
|
AppUserPrincipal principal = loadPrincipal(payload, request.getRequestURI());
|
if (!principal.getRole().equals(payload.getRole())) {
|
writeUnauthorized(response, "Token权限信息已变更");
|
return;
|
}
|
UsernamePasswordAuthenticationToken authentication =
|
new UsernamePasswordAuthenticationToken(principal, token, principal.getAuthorities());
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
filterChain.doFilter(request, response);
|
} catch (Exception exception) {
|
SecurityContextHolder.clearContext();
|
writeUnauthorized(response, "Token无效或已过期");
|
}
|
}
|
|
/** 根据 Token 端类型读取对应用户表,任何端都不会回退查询其他用户表。 */
|
private AppUserPrincipal loadPrincipal(TokenPayload payload, String requestUri) {
|
if (payload.getClientType() == ClientType.ADMIN) {
|
AdminUser user = adminUserService.findEnabledById(payload.getUserId());
|
return new AppUserPrincipal(user.getId(), user.getUsername(), user.getRoleCode(), ClientType.ADMIN,
|
adminPermissionService.getMenuCodes(user.getRoleCode()));
|
}
|
if (payload.getClientType() == ClientType.MINI) {
|
MiniUser user = miniUserService.findEnabledById(payload.getUserId());
|
return new AppUserPrincipal(user.getId(), user.getUsername(), UserRole.MINI_USER.name(), ClientType.MINI);
|
}
|
GuideUser user = allowsDisabledGuide(requestUri)
|
? guideUserService.findWorkbenchAccessibleById(payload.getUserId())
|
: guideUserService.findEnabledById(payload.getUserId());
|
return new AppUserPrincipal(user.getId(), user.getUsername(), UserRole.GUIDE_USER.name(), ClientType.GUIDE);
|
}
|
|
private boolean allowsDisabledGuide(String requestUri) {
|
return requestUri.startsWith("/api/guide/workbench")
|
|| requestUri.startsWith("/api/guide/notifications")
|
|| requestUri.equals("/api/guide/auth/logout");
|
}
|
|
private ClientType resolveExpectedClientType(String uri) {
|
if (uri.startsWith("/api/admin/")) { return ClientType.ADMIN; }
|
if (uri.startsWith("/api/mini/")) { return ClientType.MINI; }
|
if (uri.startsWith("/api/guide/")) { return ClientType.GUIDE; }
|
return null;
|
}
|
|
private String resolveToken(HttpServletRequest request) {
|
String authorization = request.getHeader("Authorization");
|
return StringUtils.hasText(authorization) && authorization.startsWith("Bearer ") ? authorization.substring(7) : null;
|
}
|
|
private void writeUnauthorized(HttpServletResponse response, String message) throws IOException {
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
objectMapper.writeValue(response.getWriter(), ApiResponse.failure(401, message));
|
}
|
}
|