xinwei #3
@ -1,8 +1,6 @@
|
|||||||
package com.xxl.job.admin.framework.security.config;
|
package com.xxl.job.admin.framework.security.config;
|
||||||
|
|
||||||
import com.xxl.job.admin.enums.ApiConstants;
|
import com.xxl.job.admin.enums.ApiConstants;
|
||||||
import com.xxl.job.admin.framework.security.core.filter.TokenAuthenticationFilter;
|
|
||||||
import com.xxl.job.admin.framework.security.core.handler.AccessDeniedHandlerImpl;
|
|
||||||
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.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
@ -16,42 +14,18 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
|
|||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Demo 模块的 Security 配置
|
* Security 配置 由于案例单点登录模块引入了spring-boot-starter-security,要配Security配置,不然会使用默认全部拦截
|
||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
@Resource
|
|
||||||
private TokenAuthenticationFilter tokenAuthenticationFilter;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AccessDeniedHandlerImpl accessDeniedHandler;
|
|
||||||
@Resource
|
|
||||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||||
// 设置 URL 安全权限
|
// 设置 URL 安全权限
|
||||||
httpSecurity.csrf().disable() // 禁用 CSRF 保护
|
httpSecurity.csrf().disable() // 禁用 CSRF 保护
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
// 1. 静态资源,可匿名访问
|
.anyRequest().permitAll(); // 放行所有,使用xxl-job的拦截器拦截
|
||||||
.antMatchers("/**").permitAll()
|
|
||||||
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
|
|
||||||
// 2. 登录相关的接口,可匿名访问
|
|
||||||
.antMatchers("/toLogin").permitAll()
|
|
||||||
.antMatchers("/auth/login-by-code").permitAll()
|
|
||||||
.antMatchers("/auth/refresh-token").permitAll()
|
|
||||||
.antMatchers("/auth/logout").permitAll()
|
|
||||||
// last. 兜底规则,必须认证
|
|
||||||
.and().authorizeRequests()
|
|
||||||
.anyRequest().authenticated();
|
|
||||||
|
|
||||||
// 设置处理器
|
|
||||||
httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
|
|
||||||
.authenticationEntryPoint(authenticationEntryPoint);
|
|
||||||
|
|
||||||
// 添加 Token Filter
|
|
||||||
httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
|
||||||
return httpSecurity.build();
|
return httpSecurity.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
package com.xxl.job.admin.framework.security.core.filter;
|
|
||||||
|
|
||||||
|
|
||||||
import com.xxl.job.admin.client.OAuth2Client;
|
|
||||||
import com.xxl.job.admin.client.dto.CommonResult;
|
|
||||||
import com.xxl.job.admin.client.dto.oauth2.OAuth2CheckTokenRespDTO;
|
|
||||||
import com.xxl.job.admin.framework.security.core.LoginUser;
|
|
||||||
import com.xxl.job.admin.framework.security.core.util.SecurityUtils;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Token 过滤器,验证 token 的有效性
|
|
||||||
* 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private OAuth2Client oauth2Client;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
|
||||||
FilterChain filterChain) throws ServletException, IOException {
|
|
||||||
// 1. 获得访问令牌
|
|
||||||
String token = SecurityUtils.obtainAuthorization(request, "Authorization");
|
|
||||||
if (StringUtils.hasText(token)) {
|
|
||||||
// 2. 基于 token 构建登录用户
|
|
||||||
LoginUser loginUser = buildLoginUserByToken(token);
|
|
||||||
// 3. 设置当前用户
|
|
||||||
if (loginUser != null) {
|
|
||||||
SecurityUtils.setLoginUser(loginUser, request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 继续过滤链
|
|
||||||
filterChain.doFilter(request, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LoginUser buildLoginUserByToken(String token) {
|
|
||||||
try {
|
|
||||||
CommonResult<OAuth2CheckTokenRespDTO> accessTokenResult = oauth2Client.checkToken(token);
|
|
||||||
OAuth2CheckTokenRespDTO accessToken = accessTokenResult.getData();
|
|
||||||
if (accessToken == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// 构建登录用户
|
|
||||||
return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
|
|
||||||
.setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
|
|
||||||
.setAccessToken(accessToken.getAccessToken());
|
|
||||||
} catch (Exception exception) {
|
|
||||||
// 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
package com.xxl.job.admin.framework.security.core.handler;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
|
||||||
import com.xxl.job.admin.client.dto.CommonResult;
|
|
||||||
import com.xxl.job.admin.framework.security.core.util.SecurityUtils;
|
|
||||||
import com.xxl.job.admin.framework.security.core.util.ServletUtils;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。
|
|
||||||
*
|
|
||||||
* 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@SuppressWarnings("JavadocReference")
|
|
||||||
@Slf4j
|
|
||||||
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
|
|
||||||
throws IOException, ServletException {
|
|
||||||
// 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
|
|
||||||
log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
|
|
||||||
SecurityUtils.getLoginUserId(), e);
|
|
||||||
// 返回 403
|
|
||||||
CommonResult<Object> result = new CommonResult<>();
|
|
||||||
result.setCode(HttpStatus.FORBIDDEN.value());
|
|
||||||
result.setMsg("没有该操作权限");
|
|
||||||
ServletUtils.writeJSON(response, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package com.xxl.job.admin.framework.security.core.handler;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
|
||||||
import com.xxl.job.admin.client.dto.CommonResult;
|
|
||||||
import com.xxl.job.admin.framework.security.core.util.ServletUtils;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.security.core.AuthenticationException;
|
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
|
|
||||||
*
|
|
||||||
* 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@Slf4j
|
|
||||||
@SuppressWarnings("JavadocReference") // 忽略文档引用报错
|
|
||||||
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
|
|
||||||
log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
|
|
||||||
// 返回 401
|
|
||||||
CommonResult<Object> result = new CommonResult<>();
|
|
||||||
result.setCode(HttpStatus.UNAUTHORIZED.value());
|
|
||||||
result.setMsg("账号未登录");
|
|
||||||
ServletUtils.writeJSON(response, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -69,17 +69,6 @@ public class SecurityUtils {
|
|||||||
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
|
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得当前用户的编号,从上下文中
|
|
||||||
*
|
|
||||||
* @return 用户编号
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static Long getLoginUserId() {
|
|
||||||
LoginUser loginUser = getLoginUser();
|
|
||||||
return loginUser != null ? loginUser.getId() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置当前用户
|
* 设置当前用户
|
||||||
*
|
*
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
package com.xxl.job.admin.framework.security.core.util;
|
|
||||||
|
|
||||||
import cn.hutool.extra.servlet.ServletUtil;
|
|
||||||
import cn.hutool.json.JSONUtil;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端工具类
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
public class ServletUtils {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回 JSON 字符串
|
|
||||||
*
|
|
||||||
* @param response 响应
|
|
||||||
* @param object 对象,会序列化成 JSON 字符串
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
|
|
||||||
public static void writeJSON(HttpServletResponse response, Object object) {
|
|
||||||
String content = JSONUtil.toJsonStr(object);
|
|
||||||
ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void write(HttpServletResponse response, String text, String contentType) {
|
|
||||||
ServletUtil.write(response, text, contentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -10,8 +10,6 @@ import com.xxl.job.admin.core.util.I18nUtil;
|
|||||||
import com.xxl.job.admin.core.util.JacksonUtil;
|
import com.xxl.job.admin.core.util.JacksonUtil;
|
||||||
import com.xxl.job.admin.dao.XxlJobUserDao;
|
import com.xxl.job.admin.dao.XxlJobUserDao;
|
||||||
import com.xxl.job.admin.framework.security.core.LoginUser;
|
import com.xxl.job.admin.framework.security.core.LoginUser;
|
||||||
import com.xxl.job.admin.framework.security.core.filter.TokenAuthenticationFilter;
|
|
||||||
import com.xxl.job.admin.framework.security.core.handler.AccessDeniedHandlerImpl;
|
|
||||||
import com.xxl.job.admin.framework.security.core.util.SecurityUtils;
|
import com.xxl.job.admin.framework.security.core.util.SecurityUtils;
|
||||||
import com.xxl.job.core.biz.model.ReturnT;
|
import com.xxl.job.core.biz.model.ReturnT;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
Loading…
Reference in New Issue
Block a user