mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-13 18:45:06 +08:00
多模块重构 2:在 yudao-admin-server 中,引入 yudao-module-member 模块
This commit is contained in:
@ -7,6 +7,9 @@ import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl;
|
||||
import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl;
|
||||
import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl;
|
||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
|
||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthService;
|
||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthServiceImpl;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@ -20,6 +23,7 @@ import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Spring Security 自动配置类,主要用于相关组件的配置
|
||||
@ -29,7 +33,7 @@ import javax.annotation.Resource;
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
public class YudaoSecurityAutoConfiguration {
|
||||
|
||||
@ -64,8 +68,8 @@ public class YudaoSecurityAutoConfiguration {
|
||||
* 退出处理类 Bean
|
||||
*/
|
||||
@Bean
|
||||
public LogoutSuccessHandler logoutSuccessHandler(SecurityAuthFrameworkService securityFrameworkService) {
|
||||
return new LogoutSuccessHandlerImpl(securityProperties, securityFrameworkService);
|
||||
public LogoutSuccessHandler logoutSuccessHandler(SecurityAuthService securityAuthService) {
|
||||
return new LogoutSuccessHandlerImpl(securityProperties, securityAuthService);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,9 +87,18 @@ public class YudaoSecurityAutoConfiguration {
|
||||
* Token 认证过滤器 Bean
|
||||
*/
|
||||
@Bean
|
||||
public JWTAuthenticationTokenFilter authenticationTokenFilter(SecurityAuthFrameworkService securityFrameworkService,
|
||||
public JWTAuthenticationTokenFilter authenticationTokenFilter(SecurityAuthService securityAuthService,
|
||||
GlobalExceptionHandler globalExceptionHandler) {
|
||||
return new JWTAuthenticationTokenFilter(securityProperties, securityFrameworkService, globalExceptionHandler);
|
||||
return new JWTAuthenticationTokenFilter(securityProperties, securityAuthService, globalExceptionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全认证的 Service Bean
|
||||
*/
|
||||
@Bean
|
||||
public SecurityAuthService securityAuthService(List<SecurityAuthFrameworkService> securityFrameworkServices,
|
||||
WebProperties webProperties) {
|
||||
return new SecurityAuthServiceImpl(securityFrameworkServices, webProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,13 +65,15 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
|
||||
*/
|
||||
@Resource
|
||||
private JWTAuthenticationTokenFilter authenticationTokenFilter;
|
||||
|
||||
/**
|
||||
* 自定义的权限映射 Bean
|
||||
*
|
||||
* @see #configure(HttpSecurity)
|
||||
*/
|
||||
@Resource
|
||||
private Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer;
|
||||
private Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry>
|
||||
authorizeRequestsCustomizer;
|
||||
|
||||
/**
|
||||
* 由于 Spring Security 创建 AuthenticationManager 对象时,没声明 @Bean 注解,导致无法被注入
|
||||
@ -89,8 +91,8 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
|
||||
*/
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService)
|
||||
.passwordEncoder(passwordEncoder);
|
||||
auth
|
||||
.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,16 +125,16 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
|
||||
// 一堆自定义的 Spring Security 处理器
|
||||
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
|
||||
.accessDeniedHandler(accessDeniedHandler).and()
|
||||
.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); // 登出
|
||||
.logout().logoutUrl(buildAdminApi("/logout")).logoutSuccessHandler(logoutSuccessHandler); // 登出
|
||||
|
||||
// 设置每个请求的权限 ①:全局共享规则
|
||||
httpSecurity.authorizeRequests()
|
||||
// 登录的接口,可匿名访问
|
||||
.antMatchers(api("/login")).anonymous()
|
||||
.antMatchers(buildAdminApi("/login")).anonymous()
|
||||
// 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
|
||||
// 文件的获取接口,可匿名访问
|
||||
.antMatchers(api("/infra/file/get/**")).anonymous()
|
||||
.antMatchers(buildAdminApi("/infra/file/get/**")).anonymous()
|
||||
// Swagger 接口文档
|
||||
.antMatchers("/swagger-ui.html").anonymous()
|
||||
.antMatchers("/swagger-resources/**").anonymous()
|
||||
@ -143,11 +145,11 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
|
||||
.antMatchers("/actuator/**").anonymous()
|
||||
// Druid 监控 TODO 芋艿:等对接了 druid admin 后,在调整下。
|
||||
.antMatchers("/druid/**").anonymous()
|
||||
// oAuth2 auth2/login/gitee
|
||||
.antMatchers(api("/auth2/login/**")).anonymous()
|
||||
.antMatchers(api("/auth2/authorization/**")).anonymous()
|
||||
// oAuth2 auth2/login/gitee TODO 芋艿:貌似可以删除
|
||||
.antMatchers(buildAdminApi("/auth2/login/**")).anonymous()
|
||||
.antMatchers(buildAdminApi("/auth2/authorization/**")).anonymous()
|
||||
.antMatchers("/api/callback/**").anonymous()
|
||||
// 设置每个请求的权限 ②:每个项目的自定义规则
|
||||
// 设置每个请求的权限 ②:每个项目的自定义规则 TODO 芋艿:改造成多个,方便每个模块自定义规则
|
||||
.and().authorizeRequests(authorizeRequestsCustomizer)
|
||||
// 设置每个请求的权限 ③:兜底规则,必须认证
|
||||
.authorizeRequests().anyRequest().authenticated()
|
||||
@ -156,8 +158,14 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
|
||||
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
private String api(String url) {
|
||||
return webProperties.getApiPrefix() + url;
|
||||
private String buildAdminApi(String url) {
|
||||
// TODO 芋艿:多模块
|
||||
return webProperties.getAdminApi().getPrefix() + url;
|
||||
}
|
||||
|
||||
private String buildAppApi(String url) {
|
||||
// TODO 芋艿:多模块
|
||||
return webProperties.getAppApi().getPrefix() + url;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,17 +2,15 @@ package cn.iocoder.yudao.framework.security.core.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
|
||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthService;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@ -30,7 +28,7 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
|
||||
private final SecurityProperties securityProperties;
|
||||
|
||||
private final SecurityAuthFrameworkService authService;
|
||||
private final SecurityAuthService authService;
|
||||
|
||||
private final GlobalExceptionHandler globalExceptionHandler;
|
||||
|
||||
@ -42,10 +40,10 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
if (StrUtil.isNotEmpty(token)) {
|
||||
try {
|
||||
// 验证 token 有效性
|
||||
LoginUser loginUser = authService.verifyTokenAndRefresh(token);
|
||||
LoginUser loginUser = authService.verifyTokenAndRefresh(request, token);
|
||||
// 模拟 Login 功能,方便日常开发调试
|
||||
if (loginUser == null) {
|
||||
loginUser = this.mockLoginUser(token);
|
||||
loginUser = this.mockLoginUser(request, token);
|
||||
}
|
||||
// 设置当前用户
|
||||
if (loginUser != null) {
|
||||
@ -67,10 +65,11 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
*
|
||||
* 注意,在线上环境下,一定要关闭该功能!!!
|
||||
*
|
||||
* @param request 请求
|
||||
* @param token 模拟的 token,格式为 {@link SecurityProperties#getTokenSecret()} + 用户编号
|
||||
* @return 模拟的 LoginUser
|
||||
*/
|
||||
private LoginUser mockLoginUser(String token) {
|
||||
private LoginUser mockLoginUser(HttpServletRequest request, String token) {
|
||||
if (!securityProperties.getMockEnable()) {
|
||||
return null;
|
||||
}
|
||||
@ -79,7 +78,7 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
return null;
|
||||
}
|
||||
Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length()));
|
||||
return authService.mockLogin(userId);
|
||||
return authService.mockLogin(request, userId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,16 +2,14 @@ package cn.iocoder.yudao.framework.security.core.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthService;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@ -26,14 +24,14 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
|
||||
|
||||
private final SecurityProperties securityProperties;
|
||||
|
||||
private final SecurityAuthFrameworkService securityFrameworkService;
|
||||
private final SecurityAuthService authService;
|
||||
|
||||
@Override
|
||||
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||
// 执行退出
|
||||
String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
|
||||
if (StrUtil.isNotBlank(token)) {
|
||||
securityFrameworkService.logout(token);
|
||||
authService.logout(request, token);
|
||||
}
|
||||
// 返回成功
|
||||
ServletUtils.writeJSON(response, CommonResult.success(null));
|
||||
|
@ -1,10 +1,11 @@
|
||||
package cn.iocoder.yudao.framework.security.core.service;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
/**
|
||||
* Security 框架 Auth Service 接口,定义 security 组件需要的功能
|
||||
* Security 框架 Auth Service 接口,定义不同用户类型的 {@link UserTypeEnum} 需要实现的方法
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@ -34,4 +35,11 @@ public interface SecurityAuthFrameworkService extends UserDetailsService {
|
||||
*/
|
||||
void logout(String token);
|
||||
|
||||
/**
|
||||
* 获得用户类型。每个用户类型,对应一个 SecurityAuthFrameworkService 实现类。
|
||||
*
|
||||
* @return 用户类型
|
||||
*/
|
||||
UserTypeEnum getUserType();
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.framework.security.core.service;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 安全认证的 Service 接口,对 security 组件提供统一的 Auth 相关的方法。
|
||||
* 主要是会基于 {@link HttpServletRequest} 参数,匹配对应的 {@link SecurityAuthFrameworkService} 实现,然后调用其方法。
|
||||
* 因此,在方法的定义上,和 {@link SecurityAuthFrameworkService} 差不多。
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface SecurityAuthService {
|
||||
|
||||
/**
|
||||
* 校验 token 的有效性,并获取用户信息
|
||||
* 通过后,刷新 token 的过期时间
|
||||
*
|
||||
* @param request 请求
|
||||
* @param token token
|
||||
* @return 用户信息
|
||||
*/
|
||||
LoginUser verifyTokenAndRefresh(HttpServletRequest request, String token);
|
||||
|
||||
/**
|
||||
* 模拟指定用户编号的 LoginUser
|
||||
*
|
||||
* @param request 请求
|
||||
* @param userId 用户编号
|
||||
* @return 登录用户
|
||||
*/
|
||||
LoginUser mockLogin(HttpServletRequest request, Long userId);
|
||||
|
||||
/**
|
||||
* 基于 token 退出登录
|
||||
*
|
||||
* @param request 请求
|
||||
* @param token token
|
||||
*/
|
||||
void logout(HttpServletRequest request, String token);
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package cn.iocoder.yudao.framework.security.core.service;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 安全认证的 Service 实现类,基于请求地址,计算到对应的 {@link UserTypeEnum} 枚举,从而拿到对应的 {@link SecurityAuthFrameworkService} 实现
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class SecurityAuthServiceImpl implements SecurityAuthService {
|
||||
|
||||
private final Map<UserTypeEnum, SecurityAuthFrameworkService> services = new HashMap<>();
|
||||
private final WebProperties properties;
|
||||
|
||||
public SecurityAuthServiceImpl(List<SecurityAuthFrameworkService> serviceList, WebProperties properties) {
|
||||
serviceList.forEach(service -> services.put(service.getUserType(), service));
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginUser verifyTokenAndRefresh(HttpServletRequest request, String token) {
|
||||
return selectService(request).verifyTokenAndRefresh(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginUser mockLogin(HttpServletRequest request, Long userId) {
|
||||
return selectService(request).mockLogin(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout(HttpServletRequest request, String token) {
|
||||
selectService(request).logout(token);
|
||||
}
|
||||
|
||||
private SecurityAuthFrameworkService selectService(HttpServletRequest request) {
|
||||
// 第一步,获得用户类型
|
||||
UserTypeEnum userType = getUserType(request);
|
||||
// 第二步,获得 SecurityAuthFrameworkService
|
||||
SecurityAuthFrameworkService service = services.get(userType);
|
||||
Assert.notNull(service, "URI({}) 用户类型({}) 找不到 SecurityAuthFrameworkService 实现类",
|
||||
request.getRequestURI(), userType);
|
||||
return service;
|
||||
}
|
||||
|
||||
private UserTypeEnum getUserType(HttpServletRequest request) {
|
||||
if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) {
|
||||
return UserTypeEnum.ADMIN;
|
||||
}
|
||||
if (request.getRequestURI().startsWith(properties.getAppApi().getPrefix())) {
|
||||
return UserTypeEnum.MEMBER;
|
||||
}
|
||||
throw new IllegalArgumentException(StrUtil.format("URI({}) 找不到匹配的用户类型", request.getRequestURI()));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user