黎星凯
2024-05-17 3520e86e2b00b9c1ee3f4fffd4ab49fe3d6c259e
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package com.iplatform.security;
 
import com.iplatform.base.DefaultUserPrincipal;
import com.iplatform.base.SecurityConstants;
import com.iplatform.base.VariableConstants;
import com.iplatform.base.util.TokenUtils;
import com.iplatform.core.TokenAwareContext;
import com.iplatform.core.TokenEntity;
import com.iplatform.model.po.S_user_core;
import com.iplatform.security.config.SecurityProperties;
import com.walker.infrastructure.ApplicationRuntimeException;
import com.walker.web.Constants;
import com.walker.web.ResponseCode;
import com.walker.web.ResponseValue;
import com.walker.web.TokenException;
import com.walker.web.TokenGenerator;
import com.walker.web.UserOnlineProvider;
import com.walker.web.UserPrincipal;
import com.walker.web.util.ServletUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
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.util.List;
 
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
 
    protected final transient Logger logger = LoggerFactory.getLogger(getClass());
 
    private TokenGenerator tokenGenerator;
    private UserOnlineProvider userOnlineProvider;
    private DefaultUserDetailsService defaultUserDetailsService;
 
    // 2023-03-28
    private SecurityProperties securityProperties;
 
    public void setSecurityProperties(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }
 
    public void setDefaultUserDetailsService(DefaultUserDetailsService defaultUserDetailsService) {
        this.defaultUserDetailsService = defaultUserDetailsService;
    }
 
    public void setUserOnlineProvider(UserOnlineProvider userOnlineProvider) {
        this.userOnlineProvider = userOnlineProvider;
    }
 
    public void setTokenGenerator(TokenGenerator tokenGenerator) {
        this.tokenGenerator = tokenGenerator;
    }
 
    @Override
    protected void doFilterInternal(HttpServletRequest request
            , HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
 
        String token = TokenUtils.getAuthorizationToken(request);
        if(token != null){
            try{
                String data = tokenGenerator.validateToken(token, VariableConstants.TOKEN_SECRET);
                logger.debug("token_data = " + data);
                String[] userIdAndKey = TokenUtils.getUserIdAndKey(data);
                if(userIdAndKey == null || userIdAndKey.length != 3){
                    throw new ApplicationRuntimeException("token携带用户信息解析错误:" + data);
                }
 
                String uuid = userIdAndKey[2];
                String loginId = userIdAndKey[1];
                DefaultUserDetails userDetails = null;
 
                // 根据token获取用户对象
                DefaultUserPrincipal userPrincipal = this.acquireAuthenticationUser(uuid);
                if(userPrincipal == null){
                    // 2022-11-15,这里要告诉客户端刷新token
                    // redis中缓存登录时间应小于token失效时间
                    userDetails = this.defaultUserDetailsService.acquireUserPrincipal(loginId);
 
                    // 2023-09-03 当前端传入的 token 是无效的情况下,如:用户已不存在,需要删除登录缓存
                    if(userDetails == null){
                        this.userOnlineProvider.removeUserPrincipal(uuid);
                        this.logger.warn("用户已不存在,删除登录状态缓存:{}", uuid);
                        return;
                    }
 
                    userPrincipal = (DefaultUserPrincipal)userDetails.getUserPrincipal();
                    logger.debug("token需要刷新: " + userPrincipal.getUserName());
                    this.tellClientRefreshToken(response, uuid, userPrincipal);
                    // 2022-11-15,以下响应代码不再需要,因为已经刷新token,重新缓存登录用户,
//                    ServletUtils.renderString(response, ResponseValue.error(ResponseCode.RE_LOGIN.getCode(), "token不存在或已过期,请重新认证"));
//                    return;
                } else {
                    userDetails = this.acquireUserDetails(userPrincipal);
                }
 
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
 
                // 2023-08-05
                TokenAwareContext.setCurrentToken(new TokenEntity(uuid, loginId));
 
            } catch (TokenException ex){
                if(ex.isExpired()){
                    ServletUtils.renderString(response, ResponseValue.error(ResponseCode.RE_LOGIN.getCode(), "token已过期,请重新获取"));
                    return;
                }
                System.out.println(ex.getTitle());
                ServletUtils.renderString(response, ResponseValue.error(ResponseCode.ERROR.getCode(), ex.getTitle()));
                return;
 
            } catch (Exception ex){
                logger.error("根据token获得登录信息错误:" + ex.getMessage(), ex);
                ServletUtils.renderString(response, ResponseValue.error(ResponseCode.ERROR.getCode(), ex.getMessage()));
                return;
            }
        }
        // 要尝试:token不存在不能访问
//        logger.warn("token不存在,无法访问系统更能");
        filterChain.doFilter(request, response);
        // 2023-08-05
        TokenAwareContext.clearCurrentToken();
    }
 
    /**
     * 通知终端刷新token,token会写入响应头中: TokenRefresh 字段。
     * @param response
     * @param uuid
     * @param userPrincipal
     */
    private void tellClientRefreshToken(HttpServletResponse response, String uuid, DefaultUserPrincipal userPrincipal){
//        String token = this.tokenGenerator.createToken(uuid, userPrincipal.getId()
//                , VariableConstants.DEFAULT_TOKEN_EXPIRED_MINUTES, VariableConstants.TOKEN_SECRET);
        // 刷新token,通常PC端会用到,移动端时间长使用几率低,因此这里找PC端配置。2023-03-28
        String token = TokenUtils.generateToken(userPrincipal.getId()
                , userPrincipal.getUserName(), uuid, this.tokenGenerator, securityProperties.getTokenExpireWeb());
        // 重新设置用户创建token时间
        userPrincipal.setLastLoginTime(System.currentTimeMillis());
        this.userOnlineProvider.cacheUserPrincipal(uuid, userPrincipal, securityProperties.getTokenExpireWeb());
        response.addHeader(Constants.TOKEN_HEADER_REFRESH, token);
        if(this.logger.isDebugEnabled()){
            logger.debug("刷新token, uuid = " + uuid + ", " + token);
        }
    }
 
    private DefaultUserDetails acquireUserDetails(UserPrincipal<S_user_core> userPrincipal){
        DefaultUserDetails userDetails = new DefaultUserDetails(userPrincipal);
//        if(userPrincipal.getUserInfo().isSupervisor()){
        if(userDetails.isSupervisor()){
            userDetails.addGrantedAuthority(SecurityConstants.ROLE_SUPER_ADMIN);
            userDetails.addGrantedAuthority(SecurityConstants.ROLE_ADMIN);
            userDetails.addGrantedAuthority(SecurityConstants.ROLE_USER);
        } else {
            // 当前数据库只有一个普通角色:2
//            userDetails.addGrantedAuthority("2");
            // 在用户登录后,缓存的就有角色ID集合,这里每次访问重新设置到对象中。2022-11-11
            List<String> roleIdList = userPrincipal.getRoleIdList();
            logger.info("缓存中获取 userPrincipal.getRoleIdList() = " + roleIdList);
            if(roleIdList != null){
                for(String roleId : roleIdList){
                    userDetails.addGrantedAuthority(roleId);
                }
            }
        }
        return userDetails;
    }
 
    /**
     * 模拟测试,后续修改为数据库方式。<p></p>
     * 从缓存中读取用户登录信息,如果换成失效为空。
     * @param uuid
     * @return
     * @date 2022-11-15
     */
    private DefaultUserPrincipal acquireAuthenticationUser(String uuid){
        /*String userId = userIdAndKey[0];
        if(userId.equals("0")){
            // 0 为超级管理员
            return MockPrincipalUtils.createSupervisor();
        } else {
            // 其他值模拟为普通用户
            return MockPrincipalUtils.createNormalUser("100");
        }*/
        // uuid
//        String loginKey = userIdAndKey[2];
        return (DefaultUserPrincipal)this.userOnlineProvider.getUserPrincipal(uuid);
    }
}