This commit is contained in:
2023-07-06 16:16:51 +08:00
parent 7e10a7877f
commit 96699e55c3
9 changed files with 106 additions and 32 deletions

View File

@ -91,6 +91,7 @@ qiaoba:
- /druid/** - /druid/**
- /resource/** - /resource/**
- /login - /login
- /getInfo
- /register - /register
- /captchaImage - /captchaImage
- /tenant/normal-list - /tenant/normal-list

View File

@ -1,7 +1,7 @@
package com.qiaoba.auth.config; package com.qiaoba.auth.config;
import com.qiaoba.api.auth.constants.SecurityConstant; import com.qiaoba.api.auth.constants.SecurityConstant;
import com.qiaoba.auth.filters.AuthenticationCoreFilter; import com.qiaoba.auth.filters.SecurityContextHolderFilter;
import com.qiaoba.auth.handler.AccessDeniedHandler; import com.qiaoba.auth.handler.AccessDeniedHandler;
import com.qiaoba.auth.handler.LogoutHandler; import com.qiaoba.auth.handler.LogoutHandler;
import com.qiaoba.auth.properties.AuthConfigProperties; import com.qiaoba.auth.properties.AuthConfigProperties;
@ -36,7 +36,7 @@ public class SpringSecurityConfig {
private final AuthConfigProperties authConfigProperties; private final AuthConfigProperties authConfigProperties;
private final AccessDeniedHandler accessDeniedHandler; private final AccessDeniedHandler accessDeniedHandler;
private final AuthenticationCoreFilter authenticationCoreFilter; private final SecurityContextHolderFilter securityContextHolderFilter;
private final LogoutHandler logoutHandler; private final LogoutHandler logoutHandler;
@ -72,8 +72,8 @@ public class SpringSecurityConfig {
// 退出处理 // 退出处理
httpSecurity.logout().logoutUrl(SecurityConstant.LOGOUT_URI).logoutSuccessHandler(logoutHandler); httpSecurity.logout().logoutUrl(SecurityConstant.LOGOUT_URI).logoutSuccessHandler(logoutHandler);
// 添加JWT filter // 添加JWT filter
httpSecurity.addFilterBefore(authenticationCoreFilter, UsernamePasswordAuthenticationFilter.class); httpSecurity.addFilterBefore(securityContextHolderFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity.addFilterBefore(authenticationCoreFilter, LogoutFilter.class); httpSecurity.addFilterBefore(securityContextHolderFilter, LogoutFilter.class);
return httpSecurity.build(); return httpSecurity.build();
} }
} }

View File

@ -1,21 +1,20 @@
package com.qiaoba.auth.filters; package com.qiaoba.auth.filters;
import com.qiaoba.api.auth.service.AuthConfigApiService;
import com.qiaoba.api.auth.constants.SecurityConstant; import com.qiaoba.api.auth.constants.SecurityConstant;
import com.qiaoba.api.auth.entity.dto.OnlineUserDto; import com.qiaoba.api.auth.entity.dto.OnlineUserDto;
import com.qiaoba.auth.properties.AuthConfigProperties; import com.qiaoba.api.auth.service.AuthConfigApiService;
import com.qiaoba.api.auth.service.OnlineUserService; import com.qiaoba.api.auth.service.OnlineUserService;
import com.qiaoba.api.auth.utils.TokenUtil; import com.qiaoba.api.auth.utils.TokenUtil;
import com.qiaoba.auth.properties.AuthConfigProperties;
import com.qiaoba.common.base.order.FilterOrder;
import com.qiaoba.common.redis.service.RedisService; import com.qiaoba.common.redis.service.RedisService;
import com.qiaoba.common.web.utils.ResponseUtil; import com.qiaoba.common.web.utils.ResponseUtil;
import com.qiaoba.common.web.utils.UriUtil; import com.qiaoba.common.web.utils.UriUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.core.annotation.Order;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
@ -26,28 +25,26 @@ import java.io.IOException;
import java.util.Objects; import java.util.Objects;
/** /**
* 鉴权核心过滤器 * 在线用户过滤器
* *
* @author ailanyin * @author ailanyin
* @version 1.0 * @version 1.0
* @since 2023-05-28 15:31:55 * @since 2023/7/6 9:14
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class AuthenticationCoreFilter extends OncePerRequestFilter { @Order(FilterOrder.ONLINE_USER_FILTER_ORDER)
public class OnlineUserFilter extends OncePerRequestFilter {
private final RedisService redisService;
private final UserDetailsService userDetailsService;
private final OnlineUserService onlineUserService;
private final AuthConfigProperties authConfigProperties; private final AuthConfigProperties authConfigProperties;
private final AuthConfigApiService authConfigApiService; private final AuthConfigApiService authConfigApiService;
private final RedisService redisService;
private final OnlineUserService onlineUserService;
private final UserDetailsService userDetailsService;
@Override @Override
protected void doFilterInternal(HttpServletRequest request, protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletResponse response, log.debug("Start run OnlineUserFilter, Uri: {}", request.getRequestURI());
FilterChain chain) throws ServletException, IOException {
log.debug("Start run AuthenticationCoreFilter, Uri: {}", request.getRequestURI());
// 白名单 放行 // 白名单 放行
for (String uri : authConfigProperties.getWhitelist()) { for (String uri : authConfigProperties.getWhitelist()) {
if (UriUtil.match(uri, request.getRequestURI())) { if (UriUtil.match(uri, request.getRequestURI())) {
@ -84,13 +81,6 @@ public class AuthenticationCoreFilter extends OncePerRequestFilter {
} }
} }
// 更新 SecurityContextHolder Authentication, 为了保证 SecurityContext 上下文中 userDetails 是最新的
if (Objects.nonNull(userDetails) && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response); chain.doFilter(request, response);
} }
} }

View File

@ -0,0 +1,55 @@
package com.qiaoba.auth.filters;
import cn.hutool.core.util.StrUtil;
import com.qiaoba.api.auth.utils.TokenUtil;
import com.qiaoba.auth.properties.AuthConfigProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
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.Objects;
/**
* 鉴权核心过滤器
*
* @author ailanyin
* @version 1.0
* @since 2023-05-28 15:31:55
*/
@RequiredArgsConstructor
@Slf4j
public class SecurityContextHolderFilter extends OncePerRequestFilter {
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
log.debug("Start run SecurityContextHolderFilter, Uri: {}", request.getRequestURI());
String username = TokenUtil.analyzeUsername(request);
if (StrUtil.isNotBlank(username)) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 更新 SecurityContextHolder Authentication, 为了保证 SecurityContext 上下文中 userDetails 是最新的
if (Objects.nonNull(userDetails) && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}

View File

@ -1,5 +1,6 @@
package com.qiaoba.auth.handler; package com.qiaoba.auth.handler;
import cn.hutool.core.util.ObjectUtil;
import com.qiaoba.api.auth.entity.LoginUser; import com.qiaoba.api.auth.entity.LoginUser;
import com.qiaoba.api.auth.service.OnlineUserService; import com.qiaoba.api.auth.service.OnlineUserService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -28,7 +29,9 @@ public class LogoutHandler implements LogoutSuccessHandler {
@Override @Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// 删除缓存中的用户信息 // 删除缓存中的用户信息
LoginUser user = (LoginUser) authentication.getPrincipal(); if (ObjectUtil.isNotEmpty(authentication) && ObjectUtil.isNotEmpty(authentication.getPrincipal())) {
onlineUserService.deleteOne(user.getUsername(), user.getDeviceSn(), true); LoginUser user = (LoginUser) authentication.getPrincipal();
onlineUserService.deleteOne(user.getUsername(), user.getDeviceSn(), true);
}
} }
} }

View File

@ -2,7 +2,8 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.qiaoba.auth.properties.AuthConfigProperties,\ com.qiaoba.auth.properties.AuthConfigProperties,\
com.qiaoba.auth.handler.AccessDeniedHandler,\ com.qiaoba.auth.handler.AccessDeniedHandler,\
com.qiaoba.auth.handler.LogoutHandler,\ com.qiaoba.auth.handler.LogoutHandler,\
com.qiaoba.auth.filters.AuthenticationCoreFilter,\ com.qiaoba.auth.filters.OnlineUserFilter,\
com.qiaoba.auth.filters.SecurityContextHolderFilter,\
com.qiaoba.auth.advice.SecurityExceptionAdvice,\ com.qiaoba.auth.advice.SecurityExceptionAdvice,\
com.qiaoba.auth.aspectj.DataScopeAspect,\ com.qiaoba.auth.aspectj.DataScopeAspect,\
com.qiaoba.auth.service.impl.OnlineUserServiceImpl,\ com.qiaoba.auth.service.impl.OnlineUserServiceImpl,\

View File

@ -0,0 +1,22 @@
package com.qiaoba.common.base.order;
/**
* 过滤器执行顺序
*
* @author ailanyin
* @version 1.0
* @since 2023/7/6 9:22
*/
public interface FilterOrder {
/**
* 动态数据源过滤器-执行顺序(最高)
*/
int DYNAMIC_DATASOURCE_FILTER_ORDER = -10000;
/**
* 在线用户过滤器
*/
int ONLINE_USER_FILTER_ORDER = -9999;
}

View File

@ -2,6 +2,7 @@ package com.qiaoba.common.web.utils;
import cn.hutool.http.ContentType; import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.qiaoba.common.base.constants.BaseConstant;
import com.qiaoba.common.base.result.AjaxResult; import com.qiaoba.common.base.result.AjaxResult;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -20,7 +21,7 @@ public class ResponseUtil {
public static void response(HttpServletResponse response, String msg) throws IOException { public static void response(HttpServletResponse response, String msg) throws IOException {
response.setStatus(HttpServletResponse.SC_OK); response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(ContentType.JSON.getValue()); response.setContentType(ContentType.JSON.getValue());
response.setCharacterEncoding("UTF-8"); response.setCharacterEncoding(BaseConstant.UTF8);
PrintWriter writer = response.getWriter(); PrintWriter writer = response.getWriter();
writer.write(msg); writer.write(msg);
writer.close(); writer.close();

View File

@ -9,6 +9,7 @@ import com.qiaoba.common.base.code.TenantErrorCode;
import com.qiaoba.common.base.constants.BaseConstant; import com.qiaoba.common.base.constants.BaseConstant;
import com.qiaoba.common.base.constants.TenantConstant; import com.qiaoba.common.base.constants.TenantConstant;
import com.qiaoba.common.base.context.BaseContext; import com.qiaoba.common.base.context.BaseContext;
import com.qiaoba.common.base.order.FilterOrder;
import com.qiaoba.common.database.config.DynamicDataSourceConfig; import com.qiaoba.common.database.config.DynamicDataSourceConfig;
import com.qiaoba.common.web.utils.ResponseUtil; import com.qiaoba.common.web.utils.ResponseUtil;
import com.qiaoba.common.web.utils.UriUtil; import com.qiaoba.common.web.utils.UriUtil;
@ -36,7 +37,7 @@ import java.util.Objects;
* @since 2023-04-25 22:48:43 * @since 2023-04-25 22:48:43
*/ */
@Component @Component
@Order(-10000) @Order(FilterOrder.DYNAMIC_DATASOURCE_FILTER_ORDER)
@Slf4j @Slf4j
public class DynamicDataSourceFilter extends OncePerRequestFilter { public class DynamicDataSourceFilter extends OncePerRequestFilter {