yuxc
2024-07-02 dd678ba29a3631558ab0c0c90b5be73ae489324b
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
package com.vci.starter.web.interceptor;
 
 
import com.vci.starter.web.annotation.controller.VciUnCheckRight;
import com.vci.starter.web.autoconfigure.SpringMVCConfig;
import com.vci.starter.web.constant.TokenKeyConstant;
import com.vci.starter.web.enumpck.ResultCodeEnum;
import com.vci.starter.web.pagemodel.BaseResult;
import com.vci.starter.web.pagemodel.SessionInfo;
import com.vci.starter.web.util.ApplicationContextProvider;
import com.vci.starter.web.util.LangBaseUtil;
import com.vci.starter.web.util.VciBaseUtil;
import com.vci.starter.web.util.WebThreadLocalUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
 
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
 
/**
 * 权限拦截器
 * @author weidy
 * @date 2019/11/7 2:32 PM
 */
//@Configuration
public class VciSecurityInterceptor implements HandlerInterceptor {
 
    /**
     * 日志对象
     */
    private Logger logger = LoggerFactory.getLogger(getClass());
 
    /**
     * 引入配置
     */
    @Autowired(required = false)
    private SpringMVCConfig springMVCConfig;
 
    /**
     * 会话,权限,token的接口
     */
    @Autowired
    private VciSessionForLoginI vciSessionForLoginI;
 
    /**
     * 执行拦截
     * @param request 请求对象
     * @param response 相应对象
     * @param handler 处理器
     * @return 是否成功,false表示失败,其他的拦截器将不会执行
     * @throws ServletException
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //从头中获取token,然后调用接口类,获取用户的信息,具体实现类可以是从redis里获取,也可以是通过其他的方式获取
        //如果头里面没有用户的token,那说明没有登录,
        String requestUri = request.getRequestURI();
        String contextPath = request.getContextPath();
        String url = requestUri.substring(contextPath.length());
        String userToken = request.getHeader(TokenKeyConstant.USER_TOKEN_KEY);
        if(StringUtils.isBlank(userToken)){
            userToken = request.getParameter(TokenKeyConstant.USER_TOKEN_KEY);
        }
        if(!(handler instanceof  HandlerMethod)){
            return true;
        }
 
        boolean unCheckLogin = false;
        if(handler instanceof  HandlerMethod) {
            HandlerMethod hm = (HandlerMethod)handler;
            Method method = hm.getMethod();
            //设置了不校验的会直接返回true
            if (method.isAnnotationPresent(VciUnCheckRight.class)) {
                unCheckLogin = true;
            }
            if (method.getDeclaringClass().isAnnotationPresent(VciUnCheckRight.class)) {
                unCheckLogin = true;
            }
        }
        if(url.endsWith(".md")){
            unCheckLogin = true;
        }
        if(unCheckLogin){
            //虽然不校验权限,但是如果token不为空,需要更新当前用户
            SessionInfo sessionInfo = getSessionInfo(userToken);
 
            if(sessionInfo != null){
                //初始化平台的token
                vciSessionForLoginI.initInvocationInfo(sessionInfo);
            }
 
            return true;
        }
        //获取配置文件中,不校验权限的路径
        List<String> unCheckUrls = new ArrayList<>();
        if(springMVCConfig !=null && springMVCConfig.getUnCheckUrls() !=null){
            unCheckUrls = springMVCConfig.getUnCheckUrls();
        }
        if(StringUtils.isBlank(userToken) && !unCheckUrls.contains(url)){
            //说明是没有用户信息的,而且也必须要校验是否登录的情况
            //firefox上传文件依然使用头里存放token
            if(logger.isErrorEnabled()) {
                logger.error("请求" + url + ",但是没有登录");
            }
            sendErrorMsg(response,"没有登录系统,请先登录",1);
            return false;
            //被踢下线由websocket直接提醒
        }else{
            SessionInfo sessionInfo = getSessionInfo(userToken);
            if(sessionInfo == null){
                //也是说明不存在,被踢下线时也获取不到session的信息了
                if(logger.isErrorEnabled()) {
                    logger.error("token值非法,或过期,或者用户已经被踢下线," + userToken);
                }
                sendErrorMsg(response,"token值非法,或过期,或者用户已经被踢下线," + userToken,1);
                return false;
            }else{
                if(!unCheckUrls.contains(url)){
                    if(vciSessionForLoginI == null){
                        //说明没办法校验
                        String msg = "请求路径"+ url +"没权限访问";
                        if(logger.isErrorEnabled()) {
                            logger.error(msg + userToken);
                        }
                        sendErrorMsg(response,msg,2);
                        return false;
                    }else{
                        //初始化平台的token
                        vciSessionForLoginI.initInvocationInfo(sessionInfo);
 
                        String systemPrivateToken = request.getHeader(TokenKeyConstant.SYSTEM_PRIVATE_KEY);
                        try {
                            if (vciSessionForLoginI.checkRequestRights(request, systemPrivateToken, sessionInfo, handler)) {
                                updateRequestTime(url,userToken);
                            }else{
                                return false;
                            }
                        }catch (Throwable e){
                            String errorMsg = LangBaseUtil.getErrorMsg(e);
                            if(logger.isErrorEnabled()) {
                                logger.error("检查请求是否符合权限出现了错误," + errorMsg, e);
                            }
                            sendErrorMsg(response,errorMsg,1);
                            return false;
                        }
                    }
                }else{
                    updateRequestTime(url,userToken);
                }
            }
        }
        return true;
    }
 
    private SessionInfo getSessionInfo(String userToken){
        SessionInfo sessionInfo = null;
        if(StringUtils.isNotBlank(userToken)){
            try{
                if(vciSessionForLoginI == null){
                    vciSessionForLoginI = ApplicationContextProvider.getBean(VciSessionForLoginI.class);
                }
 
                sessionInfo = vciSessionForLoginI.getSessionInfoByToken(userToken);
            }catch (Throwable e){
                logger.error("获取token出错",e);
            }
            if(sessionInfo!=null){
                WebThreadLocalUtil.setCurrentUserSessionInfoInThread(sessionInfo);
                WebThreadLocalUtil.setTokenInThread(TokenKeyConstant.TOKEN_KEY_PREFIX_IN_REDIS+sessionInfo.getToken());
            }
        }
        return sessionInfo;
    }
 
    /**
     * 返回错误信息
     * @param response 相应对象
     * @param errorMsg 错误信息
     * @param innerErrorCode 内部错误,1表示没有登录,2表示没有权限
     * @throws Exception
     */
    private void sendErrorMsg(HttpServletResponse response, String errorMsg, int innerErrorCode) throws Exception{
        BaseResult baseResult = BaseResult.fail(errorMsg);
        if(innerErrorCode ==1) {
            baseResult.setCode(ResultCodeEnum.UNAUTHORIZED.code);
        }else if(innerErrorCode == 2){
            baseResult.setCode(ResultCodeEnum.NOT_RIGHT.code);
        }
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(VciBaseUtil.getJSONStringWithDateFormat(baseResult));
    }
 
    /**
     * 更新最后请求时间
     * @param url 当前请求的路径
     * @param userToken 用户的token
     */
    private void updateRequestTime(String url,String userToken){
        //记录最后访问的时间
        List<String> unStorageRequestTimeUrls = new ArrayList<>();
        if(springMVCConfig !=null && springMVCConfig.getUnStorageRequestTimeUrls() != null){
            unStorageRequestTimeUrls = springMVCConfig.getUnStorageRequestTimeUrls();
        }
        if(CollectionUtils.isEmpty(unStorageRequestTimeUrls)){
            unStorageRequestTimeUrls.add("smSessionController/checkIdleTime");
        }
        String url1 = url;
        while(url1.startsWith("/")){
            url1 = url1.substring(1);
        }
        if(vciSessionForLoginI != null && !unStorageRequestTimeUrls.contains(url1)){
            vciSessionForLoginI.updateRequestTime(userToken);
        }
    }
}