liujie
2026-09-14 82a8bc62d5b9d5d607f6952329952b17eda2f0a0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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无效或已过期");
        }
    }
 
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String uri = request.getRequestURI();
        return uri.startsWith("/base/dougong/") || uri.startsWith("/api/base/dougong/")
                || "/base/dougong".equals(uri) || "/api/base/dougong".equals(uri);
    }
 
    /** 根据 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.startsWith("/api/guide/push-device")
                || 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));
    }
}