This commit is contained in:
ran.shang
2025-06-11 09:42:13 +08:00
commit 7e38b8d971
1935 changed files with 132836 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.newyetai.security.access.intercept;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import com.newyetai.common.AlitaResult;
import com.newyetai.common.exception.BusinessException;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.common.util.ResponseUtils;
import com.newyetai.common.util.UserContextHolder;
import com.newyetai.security.access.request.AuthDtoUtils;
import com.newyetai.security.expection.BadLoginException;
import com.newyetai.security.util.SecurityPropertyUtils;
import com.newyetai.security.util.SecurityUtils;
import com.newyetai.usercenter.user.constant.UserTypeEnum;
import com.newyetai.usercenter.user.login.service.UserLoginService;
import lombok.extern.slf4j.Slf4j;
/**
* 菜单安全拦截器
*
* @author on 2019-11-05
*/
@Slf4j
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
// ~ Static fields/initializers
// =====================================================================================
private static final String FILTER_APPLIED =
"__spring_security_filterSecurityInterceptor_filterApplied_2";
public static final String AUTH_URL = "/sys_security_auth";
public static final String ERROR_URL = "/error";
// ~ Instance fields
// ================================================================================================
private FilterInvocationSecurityMetadataSource securityMetadataSource;
private boolean observeOncePerRequest = true;
// ~ Methods
// ========================================================================================================
/**
* Not used (we rely on IoC container lifecycle services instead)
*
* @param arg0 ignored
*
* @throws ServletException never thrown
*/
@Override
public void init(FilterConfig arg0) throws ServletException {}
/**
* Not used (we rely on IoC container lifecycle services instead)
*/
@Override
public void destroy() {}
/**
* Method that is actually called by the filter chain. Simply delegates to the
* {@link #invoke(FilterInvocation)} method.
*
* @param request the servlet request
* @param response the servlet response
* @param chain the filter chain
*
* @throws IOException if the filter chain fails
* @throws ServletException if the filter chain fails
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return this.securityMetadataSource;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {
this.securityMetadataSource = newSource;
}
@Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
private RequestMatcher authRequestMatcher = new AntPathRequestMatcher(AUTH_URL, "POST");
private UserLoginService userLoginService;
public void setUserLoginService(UserLoginService userLoginService) {
this.userLoginService = userLoginService;
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
if (fi.getRequestUrl().equals(ERROR_URL)) {
log.info("访问资源不存在");
throw new BadLoginException("访问资源不存在");
}
// 如果是其他系统,调用用户中心鉴权
// 如果是用户中心,其他系统鉴权路径,
HttpServletRequest request = fi.getRequest();
HttpServletResponse response = fi.getResponse();
// 外部系统调用用户中心鉴权
boolean authInvoke = authRequestMatcher.matches(request);
if (authInvoke) {
FilterInvocation authFilterInvocation = AuthDtoUtils.analy(request);
try {
userLoginService.checkActive(request);
InterceptorStatusToken token = super.beforeInvocation(authFilterInvocation);
ResponseUtils.printJson(response, AlitaResult.success());
return;
} catch (BadLoginException be) {
ResponseUtils.printJson(response, be.getResult());
} catch (Exception e) {
log.error("鉴权失败", e);
ResponseUtils.printJson(response,
AlitaResult.error(AlitaResult.LOGIN_ERROR_CODE, e.getMessage()));
}
return;
}
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
boolean isAnno = auth.isAuthenticated() && SecurityPropertyUtils.isAnon(request);
boolean isUnac = !isAnno && SecurityPropertyUtils.isUnac(request);
boolean hasLogin = UserContextHolder.getLoginUser() != null;
// 匿名路径 | 未激活路径且已登录 直接过
boolean isAllowed =
(request != null) && (request.getAttribute(FILTER_APPLIED) != null) && observeOncePerRequest
|| isAnno
|| (hasLogin && isUnac);
if (isAllowed) {
// filter already applied to this request and user wants us to observe
// once-per-request handling, so don't re-do security checking
fi.getChain().doFilter(request, response);
} else {
// 未登录 去登录
Assert.isTrue(hasLogin, new BadLoginException(AlitaResult.unLogin()));
// first time this request being called, so perform security checking
if (request != null && observeOncePerRequest) {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
}
InterceptorStatusToken token = null;
// 用户中心鉴权
userLoginService.checkActive(request);
token = super.beforeInvocation(fi);
if (token == null) {
// TODO token 为空,资源未被分配,又不在白名单(只要登录就可访问),默认无权访问
// throw AccessDeniedException ;
}
try {
fi.getChain().doFilter(request, response);
} finally {
super.finallyInvocation(token);
}
super.afterInvocation(token, null);
}
}
/**
* Indicates whether once-per-request handling will be observed. By default this is
* <code>true</code>, meaning the <code>FilterSecurityInterceptor</code> will only execute
* once-per-request. Sometimes users may wish it to execute more than once per request, such as
* when JSP forwards are being used and filter security is desired on each included fragment of
* the HTTP request.
*
* @return <code>true</code> (the default) if once-per-request is honoured, otherwise
* <code>false</code> if <code>FilterSecurityInterceptor</code> will enforce
* authorizations for each and every fragment of the HTTP request.
*/
public boolean isObserveOncePerRequest() {
return observeOncePerRequest;
}
public void setObserveOncePerRequest(boolean observeOncePerRequest) {
this.observeOncePerRequest = observeOncePerRequest;
}
}

View File

@@ -0,0 +1,66 @@
package com.newyetai.security.access.metadata;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.stereotype.Component;
import com.google.common.collect.Maps;
import com.newyetai.common.entity.BaseEntity;
import com.newyetai.security.access.provider.ResourceConfigAttributeProvider;
import com.newyetai.usercenter.authority.dto.PermissionQueryDTO;
import com.newyetai.usercenter.dto.Resource;
import lombok.RequiredArgsConstructor;
/**
* 资源安全元数据源
*
* @author on 2019-11-05
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ResourceSecurityMetadataSource {
private final List<ResourceConfigAttributeProvider> providers;
public Collection<ConfigAttribute> getAttributes(Object object,
PermissionQueryDTO permissionQueryDTO) throws IllegalArgumentException {
if (!this.supports(object.getClass())) {
return Collections.emptyList();
}
Long resourceId = ((Resource) object).getId();
Collection<ConfigAttribute> attributes =
getComponentMap(object, permissionQueryDTO).get(resourceId);
return attributes == null ? Collections.emptyList() : attributes;
}
public Map<Long, Collection<ConfigAttribute>> getComponentMap(Object object,
PermissionQueryDTO permissionQueryDTO) {
AnnotationAwareOrderComparator.sort(providers);
Map<Long, Collection<ConfigAttribute>> componentMap = Maps.newLinkedHashMap();
providers.forEach(provider -> {
// 选择支持的提供者
if (!provider.supports(object.getClass())) {
return;
}
Map<Long, Collection<ConfigAttribute>> map = provider.provide(permissionQueryDTO);
if (map != null && !map.isEmpty()) {
componentMap.putAll(map);
}
});
return componentMap;
}
public boolean supports(Class<?> clazz) {
return Resource.class.isAssignableFrom(clazz);
}
}

View File

@@ -0,0 +1,76 @@
package com.newyetai.security.access.metadata;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import com.newyetai.common.util.SpringContextHolder;
import com.newyetai.security.access.provider.FilterConfigAttributeProvider;
import com.newyetai.security.access.request.AuthDtoUtils;
/**
* 菜单安全元数据源
*
* @author on 2019-11-05
*/
@Component
public class UrlFilterInvocationSecurityMetadataSource
implements FilterInvocationSecurityMetadataSource {
@Autowired
private List<FilterConfigAttributeProvider> providers;
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
/*Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : getRequestMap()
.entrySet()) {
allAttributes.addAll(entry.getValue());
}*/
return Collections.emptyList();
}
@Override
public Collection<ConfigAttribute> getAttributes(Object object) {
final HttpServletRequest request = ((FilterInvocation) object).getRequest();
Integer systemCode = AuthDtoUtils.getSystemCode(SpringContextHolder.getRequest());
return getAttributes(request, systemCode);
}
public Collection<ConfigAttribute> getAttributes(HttpServletRequest request, Integer systemCode) {
for (FilterConfigAttributeProvider provider : providers) {
for (Entry<String, Collection<ConfigAttribute>> entry : provider.provide(systemCode).entrySet()) {
if (new AntPathRequestMatcher("/" + entry.getKey(), null).matches(request)) {
return entry.getValue();
}
}
}
return Collections.emptyList();
}
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
@Autowired
public void setProvider(List<FilterConfigAttributeProvider> providers) {
AnnotationAwareOrderComparator.sort(providers);
this.providers = providers;
}
}

View File

@@ -0,0 +1,24 @@
package com.newyetai.security.access.provider;
import java.util.Collection;
import java.util.Map;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.util.matcher.RequestMatcher;
import com.newyetai.usercenter.authority.dto.PermissionQueryDTO;
/**
* 访问菜单权限信息提供者
* 资源被谁配置列表(可扩展configAttribute,可以有role_(目前),resource_等)
*
* @author on 2019-11-05
*/
public interface FilterConfigAttributeProvider {
/**
* 提供菜单权限信息,以Map结构返回,Key为路径,<br>
* Value为对应的权限信息
*
* @return 权限信息
*/
Map<String, Collection<ConfigAttribute>> provide(Integer systemCode);
}

View File

@@ -0,0 +1,25 @@
package com.newyetai.security.access.provider;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import com.newyetai.usercenter.dto.IUserDetail;
/**
* 授权信息提供者
* 人被分配角色列表(后面可扩展authority,可以有role_(目前),resource_等)
*
* @author on 2019-11-05
*/
public interface GrantedAuthorityProvider {
/**
* 提供授权权限信息
*
* @param userDetails 用户信息
* @return 授权信息
*/
Collection<? extends GrantedAuthority> provide(IUserDetail userDetails);
}

View File

@@ -0,0 +1,23 @@
package com.newyetai.security.access.provider;
import java.io.Serializable;
import org.springframework.security.access.ConfigAttribute;
/**
* 资源权限
*
* @author sr on 2020-09-04
* @param <K>
*/
public interface IConfigAttribute<K extends Serializable> extends ConfigAttribute {
/**
* 唯一标识
*
* @return
* @author sr on 2020-09-04
*/
K getKey();
}

View File

@@ -0,0 +1,15 @@
package com.newyetai.security.access.provider;
import java.io.Serializable;
/**
* 请求资源权限
*
* @author sr on 2020-09-07
* @param <K>
*/
public interface IUrlConfigAttribute<K extends Serializable> extends IConfigAttribute<K> {
String getUrl();
}

View File

@@ -0,0 +1,54 @@
package com.newyetai.security.access.provider;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.google.common.collect.Sets;
import com.newyetai.usercenter.authority.dto.PermissionQueryDTO;
/**
* 资源权限信息提供者
*
* @author on 2019-11-05
*/
public interface ResourceConfigAttributeProvider<K extends Serializable, T extends IConfigAttribute<K>> {
/**
* 提供资源权限信息
*
* @return 权限信息
*/
List<T> list(PermissionQueryDTO permissionQueryDTO);
/**
* 支持类型
*
* @param clazz
* @return
* @author sr on 2019-11-05
*/
boolean supports(Class<?> clazz);
/**
*
* @return
* @author sr on 2020-09-07
*/
default Map<K, Collection<T>> provide(PermissionQueryDTO permissionQueryDTO) {
return this.list(permissionQueryDTO)
.stream()
.collect(
Collectors.toMap(
IConfigAttribute::getKey, v -> {
Collection<T> values = Sets.newHashSet();
values.add(v);
return values;
},
(v1, v2) -> {
v1.addAll(v2);
return v1;
}));
}
}

View File

@@ -0,0 +1,42 @@
package com.newyetai.security.access.provider.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.newyetai.security.access.provider.ResourceConfigAttributeProvider;
import com.newyetai.usercenter.authority.dto.ComponentPermissionDTO;
import com.newyetai.usercenter.authority.dto.PermissionQueryDTO;
import com.newyetai.usercenter.authority.service.UcPermissionService;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.resource.dto.ComponentDTO;
/**
* 默认组件权限信息提供者
*
* @author on 2019-11-05
*/
@Order(100)
@Component
public class ComponentConfigAttributeProvider
implements ResourceConfigAttributeProvider<Long, ComponentPermissionDTO> {
@Autowired
private UcPermissionService ucPermissionService;
@Override
public boolean supports(Class<?> clazz) {
return ComponentDTO.class.isAssignableFrom(clazz);
}
@Override
@Cacheable(cacheNames = SecurityConstants.CacheName.COMPONENT_ATTRIBUTE_MAP_CACHE_KEY,
key = SecurityConstants.CACHE_KEY_PERMISSION_ORG_SYSTEM)
public List<ComponentPermissionDTO> list(PermissionQueryDTO permissionQueryDTO) {
return ucPermissionService.listComponent(permissionQueryDTO);
}
}

View File

@@ -0,0 +1,42 @@
package com.newyetai.security.access.provider.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.newyetai.security.access.provider.ResourceConfigAttributeProvider;
import com.newyetai.usercenter.authority.dto.PermissionQueryDTO;
import com.newyetai.usercenter.authority.dto.UrlPermissionDTO;
import com.newyetai.usercenter.authority.service.UcPermissionService;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.resource.dto.UrlDTO;
/**
* 默认菜单权限信息提供者
*
* @author on 2019-11-05
*/
@Component
@Order(100)
public class UrlConfigAttributeProvider
implements ResourceConfigAttributeProvider<Long, UrlPermissionDTO> {
@Autowired
private UcPermissionService ucPermissionService;
@Override
public boolean supports(Class<?> clazz) {
return UrlDTO.class.isAssignableFrom(clazz);
}
@Override
@Cacheable(cacheNames = SecurityConstants.CacheName.URL_ATTRIBUTE_MAP_CACHE_KEY,
key = SecurityConstants.CACHE_KEY_PERMISSION_ORG_SYSTEM)
public List<UrlPermissionDTO> list(PermissionQueryDTO permissionQueryDTO) {
return ucPermissionService.listUrl(permissionQueryDTO).getData();
}
}

View File

@@ -0,0 +1,60 @@
package com.newyetai.security.access.provider.impl;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.stereotype.Component;
import com.newyetai.common.AlitaResult;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.security.access.provider.FilterConfigAttributeProvider;
import com.newyetai.security.service.IPermissionSecurityService;
import com.newyetai.usercenter.authority.dto.PermissionQueryDTO;
import com.newyetai.usercenter.authority.dto.UrlPermissionDTO;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.resource.enums.UrlTypeEnum;
/**
* 默认菜单权限信息提供者
*
* @author on 2019-11-05
*/
@Component
@Order(100)
public class UrlFilterConfigAttribueProvider implements FilterConfigAttributeProvider {
@Autowired
private IPermissionSecurityService iPermissionSecurityService;
@Override
@Cacheable(cacheNames = SecurityConstants.CacheName.URL_FILTER_ATTRIBUTE_MAP_CACHE_KEY,
key = SecurityConstants.CACHE_KEY_SYSTEM)
public Map<String, Collection<ConfigAttribute>> provide(
Integer systemCode) {
// 应用下所有授权信息
PermissionQueryDTO permissionQueryDTO =
PermissionQueryDTO.builder().systemCode(systemCode).build();
AlitaResult<List<UrlPermissionDTO>> result =
iPermissionSecurityService.listUrl(permissionQueryDTO);
Assert.isTrue(result.getSuccess(), "资源权限获取失败");
return result.getData().stream().filter(p -> StringUtils.isNotBlank(p.getLink())
&& UrlTypeEnum.INTERFACE.getType().toString().equals(p.getResourceType()))
.collect(Collectors.toMap(UrlPermissionDTO::getLink, v -> {
Collection<ConfigAttribute> values = new HashSet<ConfigAttribute>();
values.add(v);
return values;
}, (v1, v2) -> {
v1.addAll(v2);
return v1;
}));
}
}

View File

@@ -0,0 +1,40 @@
package com.newyetai.security.access.provider.impl;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import com.newyetai.common.AlitaResult;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.security.access.provider.GrantedAuthorityProvider;
import com.newyetai.security.service.IRoleAllocationSecurityService;
import com.newyetai.usercenter.authority.dto.RoleAllocationDTO;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.dto.IUserDetail;
/**
* 默认用户授权信息提供者
*
* @author on 2019-11-05
*/
@Component
public class UserGrantedAuthorityProvider implements GrantedAuthorityProvider {
@Autowired
private IRoleAllocationSecurityService iRoleAllocationSecurityService;
@Override
@Cacheable(cacheNames = SecurityConstants.CacheName.USER_GRANTED_AUTHORITY_CACHE_KEY,
key = SecurityConstants.CACHE_KEY_ORG_USER)
public Collection<? extends GrantedAuthority> provide(IUserDetail userDetails) {
AlitaResult<List<RoleAllocationDTO>> result =
iRoleAllocationSecurityService.findByUserId(userDetails.getId());
Assert.isTrue(result.getSuccess(), "用户权限获取失败");
return result.getData();
}
}

View File

@@ -0,0 +1,28 @@
package com.newyetai.security.access.request;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sr on 2020-09-09
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthDTO implements Serializable{
/** **/
private static final long serialVersionUID = -6816336716189522975L;
private String url;
private String method;
private Integer systemCode;
}

View File

@@ -0,0 +1,58 @@
package com.newyetai.security.access.request;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.FilterInvocation;
import com.newyetai.common.util.EnvironmentUtils;
/**
* @author sr on 2020-09-10
*/
public abstract class AuthDtoUtils {
public static final String PARAM_URL = "url";
public static final String PARAM_METHOD = "method";
public static final String PARAM_SYSTEMCODE = "systemCode";
/**
* 转换request为认证对象
*
* @param request
* @return
* @author sr on 2020-09-10
*/
public static AuthDTO convert(HttpServletRequest request) {
return AuthDTO.builder().url(request.getServletPath()).method(request.getMethod())
.systemCode(EnvironmentUtils.getSystemCode()).build();
}
/**
* 根据请求对象解析成拦截器调用对象
*
* @param request
* @return
* @author sr on 2020-09-10
*/
public static FilterInvocation analy(HttpServletRequest request) {
return new FilterInvocation(request.getParameter(PARAM_URL),
request.getParameter(PARAM_METHOD));
}
/**
* 获取请求的应用标识
*
* @param request
* @return
* @author sr on 2020-09-10
*/
public static Integer getSystemCode(HttpServletRequest request) {
String systemCodeStr = null;
if (request == null || (systemCodeStr = request.getParameter(PARAM_SYSTEMCODE)) == null) {
return EnvironmentUtils.getSystemCode();
}
return Integer.valueOf(systemCodeStr);
}
}

View File

@@ -0,0 +1,76 @@
package com.newyetai.security.access.vote;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.vote.AbstractAccessDecisionManager;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.newyetai.security.config.SecurityProperties;
import com.newyetai.security.dto.SecurityUserDTO;
/**
* 默认访问决策管理器
*
* @author on 2019-11-05
*/
@Component
public class AccessDecisionManagerImpl extends AbstractAccessDecisionManager {
private List<AccessDecisionVoter<? extends Object>> decisionVoters;
@Autowired
public AccessDecisionManagerImpl(List<AccessDecisionVoter<? extends Object>> decisionVoters, SecurityProperties securityProperties) {
super(decisionVoters);
this.decisionVoters = decisionVoters;
this.setAllowIfAllAbstainDecisions(securityProperties.getAllowIfAllAbstainDecisions());
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
Object principal = authentication.getPrincipal();
if (principal instanceof SecurityUserDTO
&& ((SecurityUserDTO) principal).getSupeiror()) {
// 平台管理员放行
return;
}
int deny = 0;
if (!configAttributes.isEmpty()) {
for (AccessDecisionVoter voter : getDecisionVoters()) {
if (voter.supports(object.getClass())) {
int result = voter.vote(authentication, object, configAttributes);
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
}
}
if (deny > 0) {
throw new AccessDeniedException(
messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
checkAllowIfAllAbstainDecisions();
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.newyetai.security.access.vote;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
import com.newyetai.security.service.GrantedAuthorityService;
import com.newyetai.usercenter.dto.IUserDetail;
import com.newyetai.usercenter.dto.Resource;
/**
* 资源角色投票器 重写RoleVoter,获取用户授权信息authorities方式更改
*
* @author on 2019-11-05
*/
@Component
public class ResourceRoleVoter implements AccessDecisionVoter<Object> {
@Autowired
private GrantedAuthorityService grantedAuthorityService;
private String rolePrefix = "ROLE_";
public String getRolePrefix() {
return rolePrefix;
}
public void setRolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
}
@Override
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null)
&& attribute.getAttribute().startsWith(getRolePrefix())) {
return true;
} else {
return false;
}
}
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz) || Resource.class.isAssignableFrom(clazz);
}
@Override
public int vote(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes) {
if (authentication == null) {
return ACCESS_DENIED;
}
int result = ACCESS_ABSTAIN;
Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);
for (ConfigAttribute attribute : attributes) {
if (this.supports(attribute)) {
result = ACCESS_DENIED;
for (GrantedAuthority authority : authorities) {
if (attribute.getAttribute().equals(authority.getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
return result;
}
Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {
if (authentication.getPrincipal() instanceof IUserDetail) {
IUserDetail userDetails = (IUserDetail) authentication.getPrincipal();
return grantedAuthorityService.getGrantedAuthorities(userDetails);
}
return authentication.getAuthorities();
}
}

View File

@@ -0,0 +1,60 @@
package com.newyetai.security.cas;
import java.util.Collection;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
/**
* cas认证信息
*
* @author sr on 2020-06-04
*/
public class CasAuthenticationToken extends AbstractAuthenticationToken {
/** **/
private static final long serialVersionUID = 4078096826251434490L;
private final Object principal;
private String credentials;
public CasAuthenticationToken(Object principal, String credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
}
public CasAuthenticationToken(Object principal,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = null;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
credentials = null;
}
}

View File

@@ -0,0 +1,88 @@
package com.newyetai.security.config;
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.newyetai.security.handle.MyAuthenctiationFailureHandler;
import com.newyetai.security.mobile.MobileAuthenticationProvider;
import com.newyetai.security.social.service.StandardClientRegistrationRepository;
import com.newyetai.security.social.wechat.service.WechatOauthLoginProvider;
import com.newyetai.security.ssb.SsbAuthenticationProvider;
import com.newyetai.security.temptoken.TokenAuthenticationProvider;
import com.newyetai.security.upassword.UpasswordAuthenticationProvider;
import com.newyetai.security.util.SecurityPropertyUtils;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.user.login.service.UserLoginService;
/**
* security配置类
*
* @author on 2019-11-05
*/
@Configuration
@AutoConfigurationPackage
public class SecurityConfiguration {
private SecurityProperties securityProperties;
SecurityConfiguration(SecurityProperties securityProperties){
this.securityProperties = securityProperties;
SecurityPropertyUtils.setSecurityProperties(securityProperties);
}
/**
* 声明密码加密方式
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
MyAuthenctiationFailureHandler myAuthenctiationFailureHandler() {
return new MyAuthenctiationFailureHandler(securityProperties.getLoginPage());
}
@Bean
@ConditionalOnProperty(name = SecurityConstants.PROP_KEY_SECURITY_OAUTH2_USEDB, havingValue = "true",
matchIfMissing = false)
StandardClientRegistrationRepository standardClientRegistrationRepository(UserLoginService userLoginService) {
return new StandardClientRegistrationRepository(userLoginService);
}
@Bean
@ConditionalOnBean(UserLoginService.class)
MobileAuthenticationProvider mobileAuthenticationProvider(UserLoginService userLoginService) {
return new MobileAuthenticationProvider(userLoginService);
}
@Bean
@ConditionalOnBean(UserLoginService.class)
WechatOauthLoginProvider wechatOauthLoginProvider(UserLoginService userLoginService) {
return new WechatOauthLoginProvider(userLoginService);
}
@Bean
@ConditionalOnBean(UserLoginService.class)
TokenAuthenticationProvider tokenAuthenticationProvider(UserLoginService userLoginService) {
return new TokenAuthenticationProvider(userLoginService);
}
@Bean
@ConditionalOnBean(UserLoginService.class)
UpasswordAuthenticationProvider upasswordAuthenticationProvider(UserLoginService userLoginService) {
return new UpasswordAuthenticationProvider(userLoginService);
}
@Bean
@ConditionalOnBean(UserLoginService.class)
SsbAuthenticationProvider ssbAuthenticationProvider(UserLoginService userLoginService) {
return new SsbAuthenticationProvider(userLoginService);
}
}

View File

@@ -0,0 +1,76 @@
package com.newyetai.security.config;
import com.newyetai.security.jwt.JwtProperties;
import lombok.Data;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 安全相关配置项
*
* @author guhong
*/
@Component
@Data
@ConfigurationProperties(prefix = SecurityProperties.PREFIX)
public class SecurityProperties {
/** 配置前缀 **/
public final static String PREFIX = "security";
/**
* 匿名访问的url
*/
private String anonymous;
/**
* 忽略的资源,直接跳过spring security权限校验,一般是用做静态资源
*/
private String ignoring;
/** 登录页面 **/
private String loginPage;
private String loginSuccessPage;
/** h5登录成功页 **/
private String loginH5SuccessPage;
/** 登录请求路径 **/
private String loginProcessingUrl;
private Boolean allowIfAllAbstainDecisions;
/** 域名 **/
private String domain;
private Boolean superiorOnly;
/** 不记日志路径,contains匹配 **/
private String notLogs;
private JwtProperties jwt = new JwtProperties();
/** 是否调用本地认证鉴权,默认本地;调用户中心配置false **/
private Boolean useLocal;
private Oauth2Properties oauth2 = new Oauth2Properties();
public String[] getAnonymousArray() {
return StringUtils.isBlank(anonymous) ? ArrayUtils.EMPTY_STRING_ARRAY : anonymous.split(",");
}
public String[] getIgnoringArray() {
return StringUtils.isBlank(ignoring) ? ArrayUtils.EMPTY_STRING_ARRAY : ignoring.split(",");
}
@Data
public class Oauth2Properties{
/** 是否启用 **/
private Boolean enable;
/** oauth配置类型:true使用数据库配置,否则配置文件 **/
private Boolean usedb;
}
}

View File

@@ -0,0 +1,247 @@
package com.newyetai.security.config;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
import com.newyetai.security.access.intercept.FilterSecurityInterceptor;
import com.newyetai.security.handle.MyAuthenctiationFailureHandler;
import com.newyetai.security.handle.MyLoginSuccessHandler;
import com.newyetai.security.handle.MyLoginUrlAuthenticationEntryPoint;
import com.newyetai.security.handle.MyLogoutSuccessHandler;
import com.newyetai.security.jwt.JwtAuthenticationTokenFilter;
import com.newyetai.security.mobile.MobileAuthenticationFilter;
import com.newyetai.security.mobile.MobileAuthenticationProvider;
import com.newyetai.security.social.service.StandardAuthorizationCodeTokenResponseClient;
import com.newyetai.security.social.service.StandardOAuth2UserService;
import com.newyetai.security.social.service.StandardOauth2AuthorizationRequestResolver;
import com.newyetai.security.ssb.SsbAuthenticationFilter;
import com.newyetai.security.ssb.SsbAuthenticationProvider;
import com.newyetai.security.temptoken.TokenAuthenticationFilter;
import com.newyetai.security.temptoken.TokenAuthenticationProvider;
import com.newyetai.security.upassword.UpasswordAuthenticationFilter;
import com.newyetai.security.upassword.UpasswordAuthenticationProvider;
import com.newyetai.swagger.config.SwaggerConfig;
import com.newyetai.usercenter.user.login.service.UserLoginService;
import com.newyetai.usercenter.websocket.util.WebSocketUtils;
import lombok.extern.slf4j.Slf4j;
/**
* security配置
*
* @author on 2019-11-05
*/
@Slf4j
@Component
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Autowired
private SecurityProperties securityProperties;
@Autowired
private MyLogoutSuccessHandler myLogoutSuccessHandler;
@Autowired
private MyLoginSuccessHandler myLoginSuccessHandler;
@Autowired
private MyAuthenctiationFailureHandler myAuthenctiationFailureHandler;
@Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
private AccessDecisionManager accessDecisionManager;
@Autowired(required = false)
private UpasswordAuthenticationProvider upasswordAuthenticationProvider;
@Autowired(required = false)
private MobileAuthenticationProvider mobileAuthenticationProvider;
@Autowired(required = false)
private TokenAuthenticationProvider tokenAuthenticationProvider;
@Autowired
private SsbAuthenticationProvider ssbAuthenticationProvider;
@Autowired
private SwaggerConfig swaggerConfig;
@Autowired(required = false)
private UserLoginService userLoginService;
@Autowired(required = false)
private ClientRegistrationRepository standardClientRegistrationRepository;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 添加cas认证提供者
auth.authenticationProvider(upasswordAuthenticationProvider)
.authenticationProvider(mobileAuthenticationProvider)
.authenticationProvider(tokenAuthenticationProvider)
.authenticationProvider(ssbAuthenticationProvider)
;
}
@Override
public void configure(WebSecurity web) throws Exception {
String[] ignoringArray = securityProperties.getIgnoringArray();
// swagger 接口文档,不设置访问权限
if (swaggerConfig.isSwaggerEnable() && StringUtils.isNotBlank(SwaggerConfig.ACCESS_PREFIX)) {
ignoringArray = ArrayUtils.addAll(ignoringArray, SwaggerConfig.ACCESS_PREFIX.split(","));
}
// websocket
if (StringUtils.isNotBlank(WebSocketUtils.getPath())) {
ignoringArray = ArrayUtils.add(ignoringArray, WebSocketUtils.getPath());
}
// 忽略的资源,直接跳过spring security权限校验
web.ignoring()
.antMatchers(ignoringArray)/*.antMatchers(securityProperties.getAnonymousArray())*/;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
log.info("------WebSecurityConfigurer configure");
http
.logout()
.logoutSuccessHandler(myLogoutSuccessHandler).and()
// 自定义认证失败处理器
.exceptionHandling().authenticationEntryPoint(
new MyLoginUrlAuthenticationEntryPoint(securityProperties.getLoginPage()))
.and();
if (BooleanUtils.isTrue(securityProperties.getOauth2().getEnable())) {
// oauth2
http.oauth2Login()
.authorizationEndpoint()
.authorizationRequestResolver(
new StandardOauth2AuthorizationRequestResolver(
this.standardClientRegistrationRepository,
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI))
.and()
.tokenEndpoint()
.accessTokenResponseClient(new StandardAuthorizationCodeTokenResponseClient())
.and()
.userInfoEndpoint().userService(new StandardOAuth2UserService()).and()
.failureHandler(myAuthenctiationFailureHandler)
.successHandler(myLoginSuccessHandler).and();
}
http.addFilterAfter(upasswordAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
http.addFilterAfter(mobileAuthenticationFilter(), UpasswordAuthenticationFilter.class);
http.addFilterAfter(tokenAuthenticationFilter(), UpasswordAuthenticationFilter.class);
http.addFilterAfter(ssbAuthenticationFilter(), UpasswordAuthenticationFilter.class);
// 禁用CSRF
http.csrf().disable();
http.headers().frameOptions().deny();
http.headers().xssProtection().block(true);
http.headers().contentTypeOptions();
// 拦截器
FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor();
http.addFilterAfter(securityInterceptor,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.class);
http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
// 启用jwt
if (Boolean.TRUE.equals(securityProperties.getJwt().getEnable())) {
JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter =
createJwtAuthenticationTokenFilter();
http.addFilterAfter(jwtAuthenticationTokenFilter,
AnonymousAuthenticationFilter.class);
// 禁用session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
private JwtAuthenticationTokenFilter createJwtAuthenticationTokenFilter() {
return new JwtAuthenticationTokenFilter(securityProperties);
}
/**
* 拦截器
*
* @return
* @throws Exception
* @author sr on 2019-11-28
*/
private FilterSecurityInterceptor createFilterSecurityInterceptor() throws Exception {
FilterSecurityInterceptor securityInterceptor = new FilterSecurityInterceptor();
securityInterceptor.setSecurityMetadataSource(securityMetadataSource);
securityInterceptor.setAccessDecisionManager(accessDecisionManager);
// httpSecurity中shareobject共用的authenticationBuilder
securityInterceptor.setAuthenticationManager(this.authenticationManagerBean());
securityInterceptor.setUserLoginService(userLoginService);
securityInterceptor.afterPropertiesSet();
return securityInterceptor;
}
/**
* 手机号登录请求过滤器
*
* @param authenticationManager
* @return
* @author sr on 2020-06-04
* @throws Exception
*/
private MobileAuthenticationFilter mobileAuthenticationFilter() throws Exception {
MobileAuthenticationFilter filter = new MobileAuthenticationFilter();
addHandler(filter);
return filter;
}
private void addHandler(AbstractAuthenticationProcessingFilter filter) throws Exception {
filter.setAuthenticationManager(this.authenticationManagerBean());
filter.setAuthenticationSuccessHandler(myLoginSuccessHandler);
filter.setAuthenticationFailureHandler(myAuthenctiationFailureHandler);
}
/**
* 账号密码
* @return
* @throws Exception
* @author sr on 2021-06-22
*/
private UpasswordAuthenticationFilter upasswordAuthenticationFilter() throws Exception {
UpasswordAuthenticationFilter filter = new UpasswordAuthenticationFilter();
addHandler(filter);
return filter;
}
/**
* 临时token
* @return
* @throws Exception
* @author sr on 2021-06-22
*/
private TokenAuthenticationFilter tokenAuthenticationFilter() throws Exception {
TokenAuthenticationFilter filter = new TokenAuthenticationFilter();
addHandler(filter);
return filter;
}
private SsbAuthenticationFilter ssbAuthenticationFilter() throws Exception {
SsbAuthenticationFilter filter = new SsbAuthenticationFilter();
addHandler(filter);
return filter;
}
}

View File

@@ -0,0 +1,45 @@
package com.newyetai.security.decision.manager;
import java.util.Collection;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.userdetails.UserDetails;
import com.newyetai.usercenter.authority.dto.PermissionQueryDTO;
import com.newyetai.usercenter.dto.Resource;
/**
* 权限决策管理器。<br>
* 实现{@link org.springframework.security.access.SecurityMetadataSource}来<br>
* 提供自己定义资源的权限信息,就可以实现权限决策判断
*
* @author on 2019-11-05
*/
public interface SecurityDecisionManager {
/**
* 决策给定的资源,当前登录用户是否有权限
*
* @param resource 资源
* @return 结果。true为有权限,否则没有
*/
boolean decide(Resource resource, PermissionQueryDTO permissionQueryDTO);
/**
* 根据资源获取权限信息
*
* @param resource 资源
* @return 权限列表
*/
Collection<ConfigAttribute> findConfigAttributes(Resource resource, PermissionQueryDTO permissionQueryDTO);
/**
* 决策给定的资源,给定用户是否有权限
*
* @param userDetails 用户实体
* @param resource 资源
* @return 结果。true为有权限,否则没有
* @author sr on 2019-10-16
*/
boolean decide(UserDetails userDetails, Resource resource, PermissionQueryDTO permissionQueryDTO);
}

View File

@@ -0,0 +1,86 @@
package com.newyetai.security.decision.manager.impl;
import java.util.Collection;
import java.util.Collections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.newyetai.security.access.metadata.ResourceSecurityMetadataSource;
import com.newyetai.security.decision.manager.SecurityDecisionManager;
import com.newyetai.usercenter.authority.dto.PermissionQueryDTO;
import com.newyetai.usercenter.dto.Resource;
/**
* 权限决策管理器
*
* @author on 2019-11-05
*/
@Service
public class SecurityDecisionManagerImpl implements SecurityDecisionManager {
@Autowired
private ResourceSecurityMetadataSource resourceSecurityMetadataSource;
@Autowired
private AccessDecisionManager accessDecisionManager;
@Override
public boolean decide(Resource resource, PermissionQueryDTO permissionQueryDTO) {
return decide(SecurityContextHolder.getContext().getAuthentication(), resource, permissionQueryDTO);
}
@Override
public boolean decide(UserDetails userDetails, Resource resource,
PermissionQueryDTO permissionQueryDTO) {
return decide(getAuthentication(userDetails), resource, permissionQueryDTO);
}
private boolean decide(Authentication authentication, Resource resource,
PermissionQueryDTO permissionQueryDTO) {
try {
Collection<ConfigAttribute> attributes = findConfigAttributes(resource, permissionQueryDTO);
accessDecisionManager.decide(authentication, resource, attributes);
return true;
} catch (AccessDeniedException e) {
return false;
} catch (InsufficientAuthenticationException e) {
return false;
}
}
/**
* 获取用户认证信息
*
* @return
* @author sr on 2019-10-16
*/
protected Authentication getAuthentication(UserDetails userDetails) {
return userDetails != null ? new UsernamePasswordAuthenticationToken(userDetails, null)
: SecurityContextHolder.getContext().getAuthentication();
}
@Override
public Collection<ConfigAttribute> findConfigAttributes(Resource resource,
PermissionQueryDTO permissionQueryDTO) {
Collection<ConfigAttribute> attributes = resource.getAttributes();
if (CollectionUtils.isEmpty(attributes)
&& resourceSecurityMetadataSource.supports(resource.getClass())) {
return resourceSecurityMetadataSource.getAttributes(resource, permissionQueryDTO);
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,62 @@
package com.newyetai.security.dto;
import java.io.Serializable;
import java.util.Date;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.newyetai.common.validation.Create;
import com.newyetai.common.validation.Update;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 角色
*
* @author sr on 2019-11-05
*/
@ApiModel("角色")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RoleDTO implements Serializable {
/** **/
private static final long serialVersionUID = -1148469623294607563L;
@ApiModelProperty("角色id")
@NotNull(message = "角色id不能为空", groups = {Update.class})
private Long id;
@ApiModelProperty("名称")
@NotBlank(message = "角色名称不能为空", groups = {Create.class})
@Size(max = 128)
private String name;
/** 类型 **/
@JsonIgnore
private String type;
@ApiModelProperty("是否启用")
private Boolean enabled;
@ApiModelProperty("描述")
@Size(max = 64, groups = {Create.class, Update.class})
private String description;
@ApiModelProperty("标准角色类型:见role_standard_type")
private String standardType;
@ApiModelProperty("创建时间")
private Date createTime;
}

View File

@@ -0,0 +1,102 @@
package com.newyetai.security.dto;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.Sets;
import com.newyetai.usercenter.dto.IUserDetail;
import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* userDetails
*
* @author sr on 2020-06-10
*/
@NoArgsConstructor
@AllArgsConstructor
@Setter
@ToString
public class SecurityUserDTO extends UserDetailDTO implements Serializable, IUserDetail, UserDetails, OAuth2User {
/** **/
private static final long serialVersionUID = -4158044873830314665L;
/** 标签 **/
private String tag;
@JsonIgnore
private Collection<? extends GrantedAuthority> authorities;
@Override
public String getTag() {
return tag;
}
@JsonIgnore
public Boolean getAccountNonExpired() {
return super.getAccountNonExpired();
}
@JsonIgnore
public Boolean getAccountNonLocked() {
return super.getAccountNonLocked();
}
@JsonIgnore
public Boolean getCredentialsNonExpired() {
return super.getCredentialsNonExpired();
}
@Override
@JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities == null ? Sets.newHashSet() : authorities;
}
@Override
public boolean isAccountNonExpired() {
return !Boolean.FALSE.equals(getAccountNonExpired());
}
@Override
public boolean isAccountNonLocked() {
return !Boolean.FALSE.equals(getAccountNonLocked());
}
@Override
public boolean isCredentialsNonExpired() {
return !Boolean.FALSE.equals(getCredentialsNonExpired());
}
@Override
public boolean isEnabled() {
return !Boolean.FALSE.equals(getEnabled());
}
@Override
@JsonIgnore
public String getName() {
return getUsername();
}
@Override
@JsonIgnore
public Map<String, Object> getAttributes() {
return Collections.emptyMap();
}
}

View File

@@ -0,0 +1,61 @@
package com.newyetai.security.dto;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sr on 2019-10-22
*/
@ApiModel("用户信息")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserInfoDTO implements Serializable{
/** **/
private static final long serialVersionUID = -684945131805466713L;
@ApiModelProperty("账号")
private String username;
@ApiModelProperty("用户类型")
private Integer userType;
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("机构id")
private Long orgId;
@ApiModelProperty("机构名称")
private String orgName;
@ApiModelProperty("机构类型")
private String orgType;
@ApiModelProperty("电话")
private String phone;
@ApiModelProperty("邮箱")
private String email;
@ApiModelProperty("是否已初始密码")
private Boolean initialedPassword;
@ApiModelProperty("是否忽略初始密码")
private Boolean ignoreInitPwd;
@ApiModelProperty("招聘岗位类型")
private String recruitPositionType;
private String avatar;
}

View File

@@ -0,0 +1,38 @@
package com.newyetai.security.expection;
import org.springframework.security.core.AuthenticationException;
import com.newyetai.common.AlitaResult;
/**
* 登录信息异常
*
* @author sr on 2020-06-15
*/
public class BadLoginException extends AuthenticationException {
/** **/
private static final long serialVersionUID = -5461032165020284506L;
private AlitaResult<?> result;
public BadLoginException(String msg) {
super(msg);
result = AlitaResult.error(AlitaResult.LOGIN_ERROR_CODE, msg);
}
public BadLoginException(String msg, Throwable t) {
super(msg, t);
result = AlitaResult.error(AlitaResult.LOGIN_ERROR_CODE, msg);
}
public BadLoginException(AlitaResult<?> result) {
super(result.getMsg());
this.result = result;
}
public AlitaResult<?> getResult(){
return result;
}
}

View File

@@ -0,0 +1,31 @@
package com.newyetai.security.expection;
import com.newyetai.common.AlitaResult;
import com.newyetai.security.temptoken.dto.DuplUserToken;
/**
* 多个登录机构异常
*
* @author sr on 2020-06-15
*/
public class DuplicateOrgException extends BadLoginException {
/** **/
private static final long serialVersionUID = -5461032165020284506L;
/** 用户信息 **/
private final DuplUserToken duplUserToken;
public static final String MSG = "登录用户存在多机构";
public DuplicateOrgException(DuplUserToken duplUserToken) {
super(AlitaResult.error(AlitaResult.DUPLICATE_ORG_CODE, MSG, duplUserToken));
super.getResult().setErrorData(duplUserToken);
this.duplUserToken = duplUserToken;
}
public DuplUserToken getDuplUserToken() {
return this.duplUserToken;
}
}

View File

@@ -0,0 +1,38 @@
package com.newyetai.security.expection;
import java.util.List;
import com.newyetai.common.AlitaResult;
import com.newyetai.security.temptoken.dto.DuplMobileToken;
/**
* 多个登录用户异常
*
* @author sr on 2020-06-15
*/
public class DuplicateUserException extends BadLoginException {
/** **/
private static final long serialVersionUID = -5461032165020284506L;
/** 用户信息 **/
private final DuplMobileToken duplMobileToken;
public static final String MSG = "登录用户存在多用户";
public DuplicateUserException(DuplMobileToken duplMobileToken) {
super(AlitaResult.error(AlitaResult.DUPLICATE_USER_CODE,
MSG, duplMobileToken));
super.getResult().setErrorData(duplMobileToken);
this.duplMobileToken = duplMobileToken;
}
public DuplMobileToken getDuplMobileToken() {
return this.duplMobileToken;
}
public List<?> getUsers(){
return this.duplMobileToken.getUsers();
}
}

View File

@@ -0,0 +1,63 @@
package com.newyetai.security.handle;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import com.newyetai.common.AlitaResult;
import com.newyetai.common.util.RequestUtils;
import com.newyetai.common.util.ResponseUtils;
import com.newyetai.security.expection.BadLoginException;
import com.newyetai.security.expection.DuplicateOrgException;
import com.newyetai.security.expection.DuplicateUserException;
import lombok.extern.slf4j.Slf4j;
/**
* @author guhong
* @date 2019/8/25
*/
@Slf4j
public class MyAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private static final String DIABLE_MSG = "用户被禁用";
private static final String BAD_CREDENTIALS_MSG = "账号和密码不匹配,请重新登录";
public MyAuthenctiationFailureHandler(String loginFormUrl) {
super(loginFormUrl);
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
log.debug("登录失败:{}", exception);
if (exception instanceof BadLoginException) {
ResponseUtils.printJson(response, ((BadLoginException) exception).getResult());
return;
}
if (RequestUtils.isAjaxRequest(request)) {
ResponseUtils.printJson(response, AlitaResult.error(dealExceptionMsg(exception)));
} else {
super.onAuthenticationFailure(request, response, exception);
}
}
public String dealExceptionMsg(AuthenticationException exception) {
if (exception instanceof DisabledException) {
return DIABLE_MSG;
}
if (exception instanceof BadCredentialsException) {
return BAD_CREDENTIALS_MSG;
}
return exception.getMessage();
}
}

View File

@@ -0,0 +1,91 @@
package com.newyetai.security.handle;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import com.newyetai.common.AlitaResult;
import com.newyetai.common.dto.CasTokenDTO;
import com.newyetai.common.util.CookieUtils;
import com.newyetai.common.util.JsonUtils;
import com.newyetai.common.util.RequestUtils;
import com.newyetai.common.util.ResponseUtils;
import com.newyetai.security.config.SecurityProperties;
import com.newyetai.security.dto.SecurityUserDTO;
import com.newyetai.security.jwt.JwtTokenUtils;
import com.newyetai.security.ssb.SsbAuthenticationFilter;
import com.newyetai.security.util.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
/**
* 登录成功处理器
*
* @author guhong
* @date 2019/8/19
*/
@Slf4j
@Component
public class MyLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired
private SecurityProperties securityProperties;
@Autowired
private JwtTokenUtils jwtTokenUtils;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
Object principal = authentication.getPrincipal();
log.debug("------MyLoginSuccessHandler onAuthenticationSuccess userDTO: {}",
JsonUtils.toJsonString(principal));
CasTokenDTO caseTokenDTO = null;
if (principal instanceof SecurityUserDTO) {
SecurityUserDTO securityUserDto = (SecurityUserDTO) principal;
caseTokenDTO = CasTokenDTO.builder().register(securityUserDto.getRegister())
.token(jwtTokenUtils.generateToken(securityUserDto)).build();
} else if (principal instanceof CasTokenDTO) {
caseTokenDTO = (CasTokenDTO) principal;
} else {
throw new RuntimeException("数据异常");
}
String token = caseTokenDTO.getToken();
this.dealCookie(token, request, response);
if (RequestUtils.isAjaxRequest(request) || RequestUtils.isFeignRequest(request)) {
// ajax
ResponseUtils.printJson(response,
AlitaResult.success(CasTokenDTO.builder().token(token).build()));
} else {
String successPage = securityProperties.getLoginSuccessPage();
if (request.getRequestURI().contains(SsbAuthenticationFilter.LOGIN_URI)) {
successPage = securityProperties.getLoginH5SuccessPage();
}
String successToUrl =
SecurityUtils.dealSuccessUrl(request, successPage, token);
response.sendRedirect(successToUrl);
}
}
private void dealCookie(String token, HttpServletRequest request,
HttpServletResponse response) {
// 处理cookie
String domainStr = RequestUtils.getRootDomain(request);
String key = CookieUtils.getLoginTokenCookieKey();
Cookie cookie = CookieUtils.createCookie(key, token, domainStr,
null, jwtTokenUtils.getExpiration().intValue());
//写入Cookie
response.addCookie(cookie);
}
}

View File

@@ -0,0 +1,49 @@
package com.newyetai.security.handle;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import com.newyetai.common.AlitaResult;
import com.newyetai.common.util.RequestUtils;
import com.newyetai.common.util.ResponseUtils;
import com.newyetai.security.expection.BadLoginException;
/**
* 自定义认证失败处理器
* @author guhong
* @date 2019/8/25
*/
public class MyLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
/**
* @param loginFormUrl URL where the login page can be found. Should either be relative to the
* web-app context path (include a leading {@code /}) or an absolute URL.
*/
public MyLoginUrlAuthenticationEntryPoint(String loginFormUrl) {
super(loginFormUrl);
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
// ajax|feign
if (RequestUtils.isAjaxRequest(request) || RequestUtils.isFeignRequest(request)) {
if (authException instanceof BadLoginException) {
ResponseUtils.printJson(response, ((BadLoginException) authException).getResult());
return;
}
ResponseUtils.printJson(response,
AlitaResult.error(AlitaResult.LOGIN_ERROR_CODE, "鉴权失败:" + authException.getMessage()));
} else {
super.commence(request, response, authException);
}
}
}

View File

@@ -0,0 +1,37 @@
package com.newyetai.security.handle;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import com.newyetai.common.AlitaResult;
import com.newyetai.common.util.CookieUtils;
import com.newyetai.common.util.RequestUtils;
import com.newyetai.common.util.ResponseUtils;
/**
* @author guhong
*/
@Component
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//清空Cookie
CookieUtils.removeCookie(CookieUtils.getLoginTokenCookieKey(),
RequestUtils.getRootDomain(request), request, response);
// ajax登出
if (RequestUtils.isAjaxRequest(request)) {
ResponseUtils.printJson(response, AlitaResult.success());
} else {
response.sendRedirect("/");
}
}
}

View File

@@ -0,0 +1,113 @@
package com.newyetai.security.jwt;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import com.newyetai.common.AlitaResult;
import com.newyetai.common.exception.ChangeTokenException;
import com.newyetai.common.util.CookieUtils;
import com.newyetai.common.util.RequestUtils;
import com.newyetai.common.util.ResponseUtils;
import com.newyetai.common.util.StrUtils;
import com.newyetai.common.util.UserContextHolder;
import com.newyetai.security.config.SecurityProperties;
import com.newyetai.security.util.HostContextHolder;
import com.newyetai.usercenter.constants.OrgConstants;
import com.newyetai.usercenter.dto.IUserDetail;
import lombok.extern.slf4j.Slf4j;
/**
* jwt过滤器 解析token,setContext
*
* @author sr on 2019-12-21
*/
@Slf4j
public class JwtAuthenticationTokenFilter extends GenericFilterBean {
private SecurityProperties securityProperties;
public JwtAuthenticationTokenFilter(SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
IUserDetail userDto = UserContextHolder.getLoginUser();
String headerToken = request.getHeader(securityProperties.getJwt().getHeader());
String feignHeader = request.getHeader(RequestUtils.FEIGN_REQUEST_HEADER_KEY);
try {
dealHostHeader(request);
// 未登录,但有token
if (userDto == null) {
if (StrUtils.isNotNull(headerToken)) {
log.debug("analyse header toke:{}", headerToken);
userDto = JwtTokenUtils.getUserLoginDtoFromToken(headerToken);
}
String cookieToken =
CookieUtils.getCookieValue(CookieUtils.getLoginTokenCookieKey(), request.getCookies());
if (userDto == null && StrUtils.isNotNull(cookieToken)) {
log.debug("analyse cookie toke :{}", cookieToken);
userDto = JwtTokenUtils.getUserLoginDtoFromToken(cookieToken);
}
if (userDto != null) {
SecurityContextHolder.getContext()
.setAuthentication(JwtTokenUtils.getAuthentication(userDto));
UserContextHolder.setContext(userDto);
}
}
// 登录用户只有平台用户能使用: 已登录 且 平台用户 且 非内部调用
//&& !StringUtils.equals(OrgConstants.ORG_TYPE_AGENT,userDto.getOrgType()) 开放代理机构登录中台判断条件
if (!RequestUtils.FEIGN_REQUEST_HEADER_VALUE.equals(feignHeader)
&& Boolean.TRUE.equals(securityProperties.getSuperiorOnly())
&& userDto != null
&& !OrgConstants.PLATFORM_ORG_ID.equals(userDto.getOrgId())) {
ResponseUtils.printJson(response, AlitaResult.error("该用户不能使用系统"));
return;
}
chain.doFilter(request, response);
} catch (ChangeTokenException cte) {
ResponseUtils.printJson(response, cte.getResult());
} catch (Throwable e) {
throw e;
} finally {
// 清理上下文
UserContextHolder.clearContext();
HostContextHolder.clearHost();
log.debug("clear context....");
}
}
private static void dealHostHeader(HttpServletRequest request) {
if (HostContextHolder.getHost() != null) {
return;
}
String hostHeader = request.getHeader(RequestUtils.ORIGIN_HOST_HEADER_KEY);
if (StringUtils.isNoneBlank(hostHeader)) {
HostContextHolder.setHost(hostHeader);
log.debug("get request head origin_host:{}", hostHeader);
} else {
HostContextHolder.setHost(RequestUtils.getDomain(request));
}
}
}

View File

@@ -0,0 +1,22 @@
package com.newyetai.security.jwt;
import lombok.Data;
/**
* jwt属性
* @author sr on 2019-12-21
*/
@Data
public class JwtProperties {
/** 是否启用 **/
private Boolean enable;
/** 加密密钥 **/
private String secret;
/** 过期时间(秒) **/
private Long expiration;
/** token对应request header的key **/
private String header = "Authorization";
/** token发行者 **/
private String iss;
}

View File

@@ -0,0 +1,344 @@
package com.newyetai.security.jwt;
import java.util.Date;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.newyetai.common.dto.CasTokenDTO;
import com.newyetai.common.exception.ChangeTokenException;
import com.newyetai.common.util.EnvironmentUtils;
import com.newyetai.common.util.JsonUtils;
import com.newyetai.common.util.UserContextHolder;
import com.newyetai.security.dto.SecurityUserDTO;
import com.newyetai.security.util.UserLoginUtils;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.dto.IUserDetail;
import com.newyetai.usercenter.user.constant.UserTypeEnum;
import com.newyetai.usercenter.user.dto.UserDetailQueryDTO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import lombok.extern.slf4j.Slf4j;
/**
* @author sr on 2020-06-18
*/
@Component
@Slf4j
public class JwtTokenUtils {
/**
* Authorization认证开头是"bearer "
*/
private static final String BEARER = "Bearer ";
/** 默认过期时间-秒 **/
private static final Long DEFAULT_EXPIRATION = 3600 * 2L;
/** 默认密钥 **/
private static final String DEFAULT_SECRET = "ca0f2602eacd4db79e92c8f1f7ff3d66";
/** 登录信息前缀 **/
private static final String LOGIN_INFO_KEY_PREFIX = "login:info:uid:";
private static final String TAG_KEY = "tag:";
private static final String PRINCIPAL = "principal";
/** 默认 **/
private static final int DEFAULT_TAG_LENGTH = 6;
/**
* 过期时间
*
* @return
* @author sr on 2020-06-10
*/
public static Long getExpiration() {
Object time = EnvironmentUtils.getProperty(SecurityConstants.PROP_KEY_JWT_EXPIRATION);
try {
return Long.parseLong((String) time);
} catch (NumberFormatException e) {
return DEFAULT_EXPIRATION;
}
}
/**
* 密钥
*
* @return
* @author sr on 2020-06-10
*/
public static String getSecret() {
String secret = (String) EnvironmentUtils.getProperty(SecurityConstants.PROP_KEY_JWT_SECRET);
return Strings.isNullOrEmpty(secret) ? DEFAULT_SECRET : secret;
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private static String generateTokenByClaims(Map<String, Object> claims) {
Date expirationDate =
new Date(System.currentTimeMillis() + getExpiration() * 1000);
return Jwts.builder().setClaims(claims).setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, getSecret().getBytes())
.compact();
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
private static Claims getClaimsFromToken(String token) {
try {
if (token.startsWith(BEARER)) {
token = StringUtils.substring(token, BEARER.length());
}
return Jwts.parser().setSigningKey(getSecret().getBytes())
.parseClaimsJws(token).getBody();
} catch (SecurityException | MalformedJwtException e) {
log.info("Invalid JWT signature.");
log.trace("Invalid JWT signature trace: {}", e);
} catch (ExpiredJwtException e) {
Claims claims = e.getClaims();
SecurityUserDTO securityUserDTO = JsonUtils.objToClassObj(claims.get(PRINCIPAL), SecurityUserDTO.class);
if (UserTypeEnum.PERSON.getType().equals(securityUserDTO.getType())) {
changeToken(securityUserDTO.getId());
}
log.info("Expired JWT token.");
log.trace("Expired JWT token trace: {}", e);
} catch (UnsupportedJwtException e) {
log.info("Unsupported JWT token.");
log.trace("Unsupported JWT token trace: {}", e);
} catch (IllegalArgumentException e) {
log.info("JWT token compact of handler are invalid.");
log.trace("JWT token compact of handler are invalid trace: {}", e);
}
return null;
}
/**
* 根据securityUserDetail登录用户对象生成token
*
* @param securityUserDTO
* @return
* @author sr on 2020-06-19
*/
public static String generateToken(SecurityUserDTO securityUserDTO) {
try {
if (securityUserDTO == null) {
return null;
}
Map<String, Object> claims = Maps.newHashMap();
claims.put("created", new Date());
claims.put(PRINCIPAL, securityUserDTO);
return generateTokenByClaims(claims);
} catch (Exception e) {
log.info("generateToken error : {}", e.getMessage());
return null;
}
}
/**
* 根据登录用户生成token
*
* @return
* @author sr on 2020-06-19
*/
public String generateTokenByUserDetail(IUserDetail userDetail) {
return generateToken((SecurityUserDTO) userDetail);
}
/**
* 根据当前登录用户生成token
*
* @return
* @author sr on 2020-06-19
*/
public String generateTokenByCurrentLoginUser() {
return this.generateTokenByUserDetail(UserContextHolder.getLoginUser());
}
/**
* 根据用户id获取用户信息生成token
*
* @param userId
* @return
* @author sr on 2020-06-19
*/
@Deprecated
public String generateToken(Long userId) {
return generateToken(UserDetailQueryDTO.builder().userId(userId).build());
}
/**
* 根据登录关键信息获取登录对象,生成token
*
* @param userDetailQueryDto
* @return
* @author sr on 2020-06-19
*/
public static String generateToken(UserDetailQueryDTO userDetailQueryDto) {
return generateToken(UserLoginUtils.loadUserToMock(userDetailQueryDto));
}
/**
* 替换token(个人)
* @param userId
* @throws ChangeTokenException
* @author sr on 2021-08-10
*/
public static void changeToken(Long userId)
throws ChangeTokenException {
changeToken(userId, null);
}
/**
* 替换token(企业)
* @param userDetailQueryDto
* @throws ChangeTokenException
* @author sr on 2021-07-07
*/
public static void changeToken(Long userId, Long orgId)
throws ChangeTokenException {
String token = generateToken(UserDetailQueryDTO.builder().userId(userId).orgId(orgId).build());
throw new ChangeTokenException(CasTokenDTO.builder().userId(userId).token(token).build());
}
/**
* 根据token解析登录用户对象信息
*
* @param token
* @return securityUserDetail
* @author sr on 2020-06-19
*/
public static SecurityUserDTO getUserLoginDtoFromToken(String token) {
Claims claims = getClaimsFromToken(token);
if (claims == null) {
return null;
}
return JsonUtils.objToClassObj(claims.get(PRINCIPAL), SecurityUserDTO.class);
}
/**
* 从令牌中获取认证信息
*
* @param token
* @return
*/
public static Authentication getAuthentication(IUserDetail securityUserDTO) {
try {
if (securityUserDTO == null) {
return null;
}
// TODO 凭证暂为null
Object credentials = null;
return new UsernamePasswordAuthenticationToken(securityUserDTO,
credentials == null ? "N/A" : credentials, null);
} catch (Exception e) {
log.info("getAuthenticationFromToken error : {}", e.getMessage());
}
return null;
}
/**
* 获取当前登录用户
*
* @param auth
* @return
* @author sr on 2020-06-16
*/
public static IUserDetail getLoginUser(Authentication auth) {
Object principal = auth == null ? null : auth.getPrincipal();
if (principal != null && principal instanceof IUserDetail) {
try {
return (IUserDetail) principal;
} catch (Exception e) {
log.info("context user not userDetail...");
return null;
}
}
return null;
}
/**
* 从令牌中获取账号
*
* @param token 令牌
* @return 账号
*/
public String getUsernameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 判断令牌是否过期
*
* @param token 令牌
* @return 是否过期
*/
public Boolean isTokenExpired(String token) {
try {
Claims claims = getClaimsFromToken(token);
Date expiration = claims.getExpiration();
return expiration.before(new Date());
} catch (Exception e) {
return false;
}
}
/**
* 刷新令牌
*
* @param token 原令牌
* @return 新令牌
*/
public String refreshToken(String token) {
String refreshedToken;
try {
Claims claims = getClaimsFromToken(token);
claims.put("created", new Date());
refreshedToken = generateTokenByClaims(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
/**
* 获取登录Redis存储的key key=login:uid:${userId}
*
* @param userId 用户ID
* @param tag 唯一标示
* @return
*/
public String getLoginInfoRedisKey(Long userId, String tag) {
log.info("------getLoginRedisKey userId: {}, tag: {}", userId, tag);
return LOGIN_INFO_KEY_PREFIX + userId + TAG_KEY + tag;
}
}

View File

@@ -0,0 +1,24 @@
package com.newyetai.security.mapstruct;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import com.newyetai.usercenter.auth.dto.AuthDetailLoginDTO;
import com.newyetai.usercenter.auth.wechat.dto.WechatAuthUserInfoDTO;
@Mapper
public interface AuthDetailMapMapper{
AuthDetailMapMapper INSTANCE = Mappers.getMapper(AuthDetailMapMapper.class);
@Mappings(value = {
@Mapping(target = "avatar", source = "headimgurl"),
@Mapping(target = "gender", source = "sex"),
@Mapping(target = "uniqueId", source = "unionid"),
})
AuthDetailLoginDTO toDto(WechatAuthUserInfoDTO wechatDto);
}

View File

@@ -0,0 +1,29 @@
package com.newyetai.security.mapstruct;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import com.newyetai.security.dto.SecurityUserDTO;
import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
/**
* @author sr on 2020-06-08
*/
@Mapper
public interface UserMapMapper {
UserMapMapper INSTANCE = Mappers.getMapper(UserMapMapper.class);
/**
* to dto
*
* @param userDto
* @return
* @author sr on 2020-06-08
*/
@Mapping(target = "tag", expression = "java(com.newyetai.common.util.RandomUtils.getRandomString(6))")
SecurityUserDTO dtoToDto(UserDetailDTO userDto);
}

View File

@@ -0,0 +1,78 @@
package com.newyetai.security.mobile;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.security.expection.BadLoginException;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.user.login.dto.LoginPhoneDTO;
/**
* 手机号认证过滤器,指定请求组装token,调起认证
*
* @author sr on 2020-06-04
*/
public class MobileAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private boolean postOnly = true;
public static final String REQUEST_METHOD = "POST";
public static final AntPathRequestMatcher requiresAuthenticationRequestMatcher =
new AntPathRequestMatcher("/login_phone", REQUEST_METHOD);
public MobileAuthenticationFilter() {
super(requiresAuthenticationRequestMatcher);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !REQUEST_METHOD.equals(request.getMethod())) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String userTypeStr = request.getParameter(SecurityConstants.LOGIN_USER_TYPE_KEY);
Assert.isTrue(StringUtils.isNotBlank(userTypeStr), new BadLoginException("用户类型不能为空"));
Integer userType = null;
try {
userType = Integer.valueOf(userTypeStr);
} catch (NumberFormatException e) {
throw new BadLoginException("用户类型错误:" + userTypeStr);
}
String phone = request.getParameter(SecurityConstants.LOGIN_PHONE_KEY);
String code = request.getParameter(SecurityConstants.LOGIN_CODE_KEY);
String registerCode = request.getParameter(SecurityConstants.LOGIN_REGISTER_CODE_KEY);
String entrypoint = request.getParameter(SecurityConstants.LOGIN_ENTRYPOINT_KEY);
phone = phone == null ? "" : phone.trim();
code = code == null ? "" : code.trim();
AbstractAuthenticationToken authRequest = new MobileAuthenticationToken(
LoginPhoneDTO.builder().phone(phone).code(code).userType(userType)
.registerCode(registerCode).entrypoint(entrypoint).build(),
code);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}

View File

@@ -0,0 +1,65 @@
package com.newyetai.security.mobile;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.common.validation.util.ValidateUtils;
import com.newyetai.security.dto.SecurityUserDTO;
import com.newyetai.security.expection.BadLoginException;
import com.newyetai.security.mapstruct.UserMapMapper;
import com.newyetai.usercenter.user.login.dto.LoginPhoneDTO;
import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
import com.newyetai.usercenter.user.login.service.UserLoginService;
/**
* 手机号认证provider,比对信息是否匹配
*
* @author sr on 2020-06-04
*/
public class MobileAuthenticationProvider implements AuthenticationProvider {
private UserLoginService userLoginService;
public MobileAuthenticationProvider(UserLoginService userLoginService) {
this.userLoginService = userLoginService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
LoginPhoneDTO loginPhoneDTO = (LoginPhoneDTO) authentication.getPrincipal();
String errMsg = ValidateUtils.validate(loginPhoneDTO);
Assert.isTrue(errMsg == null, new BadLoginException(errMsg));
SecurityUserDTO loadedUser = null;
try {
UserDetailDTO userDetailDTO = this.userLoginService.loadUser(loginPhoneDTO);
loadedUser = UserMapMapper.INSTANCE.dtoToDto(userDetailDTO);
} catch (BadLoginException be) {
throw be;
} catch (Exception e) {
throw new BadLoginException(e.getMessage(), e);
}
MobileAuthenticationToken result =
new MobileAuthenticationToken(loadedUser, listUserGrantedAuthorities(loadedUser));
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return (MobileAuthenticationToken.class.isAssignableFrom(authentication));
}
private Set<GrantedAuthority> listUserGrantedAuthorities(UserDetails userDetails) {
// 什么都不用做,访问资源鉴权的时会拿userDetail去缓存获取对应权限
return new HashSet<GrantedAuthority>();
}
}

View File

@@ -0,0 +1,25 @@
package com.newyetai.security.mobile;
import java.util.Collection;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
/**
* 手机号认证信息
*
* @author sr on 2020-06-04
*/
public class MobileAuthenticationToken extends UsernamePasswordAuthenticationToken {
public MobileAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public MobileAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}

View File

@@ -0,0 +1,26 @@
package com.newyetai.security.service;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import com.newyetai.usercenter.dto.IUserDetail;
/**
* 主要提供用户的授权信息的收集功能
*
* @author on 2019-11-05
*/
public interface GrantedAuthorityService {
/**
* 获取用户的授权信息
*
* @param userDetails 用户信息
* @return 权限信息
*/
Collection<? extends GrantedAuthority> getGrantedAuthorities(IUserDetail userDetails);
}

View File

@@ -0,0 +1,22 @@
package com.newyetai.security.service;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import com.newyetai.common.AlitaResult;
import com.newyetai.usercenter.authority.dto.PermissionQueryDTO;
import com.newyetai.usercenter.authority.dto.UrlPermissionDTO;
import io.swagger.annotations.ApiOperation;
/**
* @author sr on 2020-09-07
*/
public interface IPermissionSecurityService {
@ApiOperation("查询角色权限分配菜单接口")
@GetMapping("/permission/list_url")
AlitaResult<List<UrlPermissionDTO>> listUrl(PermissionQueryDTO permissionQueryDTO);
}

View File

@@ -0,0 +1,51 @@
package com.newyetai.security.service;
import java.util.List;
import com.newyetai.usercenter.dto.IUserDetail;
import com.newyetai.usercenter.resource.dto.ComponentDTO;
import com.newyetai.usercenter.resource.dto.UrlDTO;
/**
* @author sr on 2020-09-08
*/
public interface IResourceSecurityService {
/**
* 查询当前登录用户资源树状列表(包含菜单组件)
* @param systemCode
* @return
* @author sr on 2019-10-15
*/
List<UrlDTO> listResource(Integer systemCode, IUserDetail iUserDetail);
/**
* 获取当前登录用户指定角色的资源树状列表(包含菜单组件)
* @param systemCode
* @param roleId
* @return
* @author sr on 2019-10-16
*/
List<UrlDTO> listResource(Integer systemCode, IUserDetail iUserDetail, Long roleId);
/**
* 根据应用+机构获取url资源
*
* @param systemCode
* @param orgId
* @return
* @author sr on 2020-09-08
*/
List<UrlDTO> listUrlDTO(Integer systemCode, Long orgId);
/**
* 根据应用+机构获取Component资源
*
* @param systemCode
* @param orgId
* @return
* @author sr on 2020-09-08
*/
List<ComponentDTO> listComponentDTO(Integer systemCode, Long orgId);
}

View File

@@ -0,0 +1,22 @@
package com.newyetai.security.service;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.newyetai.common.AlitaResult;
import com.newyetai.usercenter.authority.dto.RoleAllocationDTO;
import io.swagger.annotations.ApiOperation;
/**
* @author sr on 2020-09-07
*/
public interface IRoleAllocationSecurityService {
@ApiOperation("查询用户分配的角色")
@GetMapping("/role/findByUserId/{userId}")
AlitaResult<List<RoleAllocationDTO>> findByUserId(@PathVariable("userId") Long userId);
}

View File

@@ -0,0 +1,20 @@
package com.newyetai.security.service;
import org.springframework.web.bind.annotation.PostMapping;
import com.newyetai.common.AlitaResult;
import com.newyetai.usercenter.user.dto.UserDetailQueryDTO;
import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
import io.swagger.annotations.ApiOperation;
/**
* @author sr on 2020-09-07
*/
public interface IUserSecurityService {
@ApiOperation("根据登录信息获取用户信息")
@PostMapping("/anon/user/find_user_detail_dto")
AlitaResult<UserDetailDTO> findUserDetailDTOResult(UserDetailQueryDTO userDetailQueryDto);
}

View File

@@ -0,0 +1,56 @@
package com.newyetai.security.service.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import com.newyetai.security.access.provider.GrantedAuthorityProvider;
import com.newyetai.security.service.GrantedAuthorityService;
import com.newyetai.usercenter.dto.IUserDetail;
/**
* 主要提供用户的授权信息的收集功能
*
* @author on 2019-11-05
*/
@Service
@Transactional(readOnly = true)
public class GrantedAuthorityServiceImpl implements GrantedAuthorityService, InitializingBean {
@Autowired
private List<GrantedAuthorityProvider> providers;
/**
* 获取用户的授权信息
*
* @param userDetails 用户信息
* @return 权限信息
*/
@Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(IUserDetail userDetails) {
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
for (GrantedAuthorityProvider provider : providers) {
Collection<? extends GrantedAuthority> list = provider.provide(userDetails);
if (list != null && !list.isEmpty()) {
grantedAuthorities.addAll(list);
}
}
return grantedAuthorities;
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notEmpty(providers, "providers can not be empty");
AnnotationAwareOrderComparator.sort(providers);
}
}

View File

@@ -0,0 +1,104 @@
package com.newyetai.security.social.service;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import com.newyetai.security.ssb.SsbAuthenticationFilter;
import com.newyetai.usercenter.auth.constant.AuthTypeEnum;
public enum CommonOAuth2Provider {
WECHAT {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId,
ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
builder.scope("snsapi_userinfo");
builder.authorizationUri("https://open.weixin.qq.com/connect/oauth2/authorize");
builder.tokenUri("https://api.weixin.qq.com/sns/oauth2/access_token");
builder.userInfoUri("https://api.weixin.qq.com/sns/userinfo");
builder.userNameAttributeName("openid");
builder.clientName(AuthTypeEnum.WECHAT.name());
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
return builder;
}
},
SSBAPP {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId,
ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
builder.authorizationUri("https://api.eshimin.com/newOauth/authorize");
builder.tokenUri("https://api.eshimin.com/newOauth/accessToken");
builder.userInfoUri("https://api.eshimin.com/newOauth/userInfo");
builder.clientName(AuthTypeEnum.SSB_APP.name());
builder.scope("read");
builder.redirectUriTemplate("{baseUrl}/" + SsbAuthenticationFilter.LOGIN_URI);
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
return builder;
}
},
WECHAT_LITE {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId,
ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
builder.clientName(AuthTypeEnum.WECHAT_LITE.name());
return builder;
}
},
ALIPAY {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId,
ClientAuthenticationMethod.POST, DEFAULT_REDIRECT_URL);
builder.clientName(AuthTypeEnum.ALIPAY.name());
return builder;
}
},
ALIPAY_LITE {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId,
ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
builder.clientName(AuthTypeEnum.ALIPAY_LITE.name());
return builder;
}
},
;
private static final String DEFAULT_REDIRECT_URL =
"{baseUrl}/{action}/oauth2/code/{registrationId}";
protected final ClientRegistration.Builder getBuilder(String registrationId,
ClientAuthenticationMethod method, String redirectUri) {
ClientRegistration.Builder builder = ClientRegistration.withRegistrationId(registrationId);
builder.clientAuthenticationMethod(method);
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
builder.redirectUriTemplate(redirectUri);
return builder;
}
/**
* Create a new
* {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder
* ClientRegistration.Builder} pre-configured with provider defaults.
* @param registrationId the registration-id used with the new builder
* @return a builder instance
*/
public abstract ClientRegistration.Builder getBuilder(String registrationId);
}

View File

@@ -0,0 +1,66 @@
package com.newyetai.security.social.service;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.RequestEntity;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.user.OAuth2User;
public interface IOauthProvider {
String getName();
/**
* 修改默认auth的请求地址
*
* @param request
* @param authorizationRequest
* @return
* @author sr on 2021-06-28
*/
default OAuth2AuthorizationRequest decorateRequest(HttpServletRequest request,
OAuth2AuthorizationRequest authorizationRequest) {
return authorizationRequest;
}
default OAuth2AccessTokenResponse getTokenResponse(
OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
return null;
}
/**
* 获取token转换器(无则OAuth2AuthorizationCodeGrantRequestEntityConverter)
* @return
* @author sr on 2021-06-28
*/
default Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> getTokenConvert(){
return null;
}
/**
* 获取用户转换器(无则OAuth2UserRequestEntityConverter)
* @return
* @author sr on 2021-06-28
*/
default Converter<OAuth2UserRequest, RequestEntity<?>> getUserConvert(){
return null;
}
/**
* 处理第三方用户信息,获取登录用户
* @param userRequest
* @param userJsonStr
* @return
* @author sr on 2021-06-28
*/
default OAuth2User dealUser(OAuth2UserRequest userRequest, String userJsonStr) {
return null;
}
}

View File

@@ -0,0 +1,74 @@
package com.newyetai.security.social.service;
import java.util.Map;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.core.convert.ConversionException;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.util.StringUtils;
import com.google.common.collect.Maps;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.common.util.JsonUtils;
import com.newyetai.usercenter.auth.constant.AuthTypeEnum;
import com.newyetai.usercenter.auth.dto.AuthClientDTO;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OAuth2ClientRegistrationAdapter {
private static Map<String, CommonOAuth2Provider> providers = Maps.newConcurrentMap();
private OAuth2ClientRegistrationAdapter() {}
public static ClientRegistration getClientRegistration(String registrationId,
AuthTypeEnum authTypeEnum,
AuthClientDTO authClientDTO) {
Assert.isTrue(authTypeEnum != null, new IllegalArgumentException("非法授权标识"));
OAuth2ClientProperties.Registration registration =
JsonUtils.toObject(authClientDTO.getParamData(), OAuth2ClientProperties.Registration.class);
return getClientRegistration(registrationId, authTypeEnum, registration);
}
private static ClientRegistration getClientRegistration(String registrationId,
AuthTypeEnum authTypeEnum,
OAuth2ClientProperties.Registration registration) {
Assert.isTrue(authTypeEnum != null, new IllegalArgumentException("非法授权标识"));
Builder builder = getBuilder(authTypeEnum.name()).getBuilder(registrationId);
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(registration::getClientId).to(builder::clientId);
map.from(registration::getClientSecret).to(builder::clientSecret);
map.from(registration::getClientAuthenticationMethod).as(ClientAuthenticationMethod::new)
.to(builder::clientAuthenticationMethod);
map.from(registration::getAuthorizationGrantType).as(AuthorizationGrantType::new)
.to(builder::authorizationGrantType);
map.from(registration::getRedirectUri).to(builder::redirectUriTemplate);
map.from(registration::getScope).as((scope) -> StringUtils.toStringArray(scope))
.to(builder::scope);
map.from(registration::getClientName).to(builder::clientName);
return builder.build();
}
public static CommonOAuth2Provider getBuilder(String authTypeName) {
CommonOAuth2Provider provider = providers.get(authTypeName);
if (provider == null) {
try {
provider =
ApplicationConversionService.getSharedInstance().convert(authTypeName,
CommonOAuth2Provider.class);
providers.put(authTypeName, provider);
} catch (ConversionException ex) {
log.error("授权类型标识错误,未找到对应的oauth2提供者:{}", authTypeName);
throw new IllegalArgumentException("授权类型标识错误,未找到对应的oauth2提供者:" + authTypeName, ex);
}
}
return provider;
}
}

View File

@@ -0,0 +1,103 @@
package com.newyetai.security.social.service;
import java.util.Arrays;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequestEntityConverter;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
import com.newyetai.security.social.util.Oauth2ProviderUtils;
/**
* code获取token
*
* @author sr on 2021-01-12
*/
public class StandardAuthorizationCodeTokenResponseClient
implements OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
public static final String INVALID_TOKEN_RESPONSE_ERROR_CODE = "invalid_token_response";
public static final RestOperations restOperations;
private Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> requestEntityConverter =
new OAuth2AuthorizationCodeGrantRequestEntityConverter();
static {
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter(),
new StringHttpMessageConverter()));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
restOperations = restTemplate;
}
@Override
public OAuth2AccessTokenResponse getTokenResponse(
OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
Assert.notNull(authorizationCodeGrantRequest, "authorizationCodeGrantRequest cannot be null");
// 不同类型不同请求体
OAuth2AccessTokenResponse tokenResponse = Oauth2ProviderUtils
.getProvider(authorizationCodeGrantRequest.getClientRegistration().getClientName())
.getTokenResponse(authorizationCodeGrantRequest);
if (tokenResponse != null) {
return tokenResponse;
}
RequestEntity<?> request = this.requestEntityConverter.convert(authorizationCodeGrantRequest);
ResponseEntity<OAuth2AccessTokenResponse> response;
try {
response = restOperations.exchange(request, OAuth2AccessTokenResponse.class);
} catch (RestClientException ex) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE,
"An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: "
+ ex.getMessage(),
null);
throw new OAuth2AuthorizationException(oauth2Error, ex);
}
tokenResponse = response.getBody();
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {
// As per spec, in Section 5.1 Successful Access Token Response
// https://tools.ietf.org/html/rfc6749#section-5.1
// If AccessTokenResponse.scope is empty, then default to the scope
// originally requested by the client in the Token Request
tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse)
.scopes(authorizationCodeGrantRequest.getClientRegistration().getScopes())
.build();
}
return tokenResponse;
}
/**
* Sets the {@link Converter} used for converting the {@link OAuth2AuthorizationCodeGrantRequest}
* to a {@link RequestEntity} representation of the OAuth 2.0 Access Token Request.
*
* @param requestEntityConverter the {@link Converter} used for converting to a {@link RequestEntity} representation of the Access Token Request
*/
public void setRequestEntityConverter(
Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> requestEntityConverter) {
Assert.notNull(requestEntityConverter, "requestEntityConverter cannot be null");
this.requestEntityConverter = requestEntityConverter;
}
}

View File

@@ -0,0 +1,50 @@
package com.newyetai.security.social.service;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.usercenter.auth.constant.AuthTypeEnum;
import com.newyetai.usercenter.auth.dto.AuthClientDTO;
import com.newyetai.usercenter.auth.dto.AuthClientQueryDTO;
import com.newyetai.usercenter.user.login.service.UserLoginService;
public class StandardClientRegistrationRepository implements ClientRegistrationRepository {
private UserLoginService userLoginService;
public StandardClientRegistrationRepository(UserLoginService userLoginService) {
this.userLoginService = userLoginService;
}
@Override
public ClientRegistration findByRegistrationId(String registrationId) {
Assert.isTrue(StringUtils.isNotBlank(registrationId),
new IllegalArgumentException("oauth授权注册标识不能为空"));
AuthClientDTO authClientDTO = userLoginService
.findAuthClientDTO(AuthClientQueryDTO.builder().id(Long.valueOf(registrationId)).build());
AuthTypeEnum authTypeEnum = AuthTypeEnum.getByCode(authClientDTO.getAuthTypeCode());
return OAuth2ClientRegistrationAdapter.getClientRegistration(authClientDTO.getId().toString(),
authTypeEnum, authClientDTO);
}
public ClientRegistration findByRegistrationId(String authTypeName, String domainOrIp) {
AuthTypeEnum authTypeEnum = AuthTypeEnum.valueOf(authTypeName.toUpperCase());
return this.findByRegistrationId(authTypeEnum, domainOrIp);
}
public ClientRegistration findByRegistrationId(Integer authTypeCode, String domainOrIp) {
AuthTypeEnum authTypeEnum = AuthTypeEnum.getByCode(authTypeCode);
return this.findByRegistrationId(authTypeEnum, domainOrIp);
}
private ClientRegistration findByRegistrationId(AuthTypeEnum authTypeEnum, String domainOrIp) {
AuthClientDTO authClientDTO = userLoginService.findAuthClientDTO(
AuthClientQueryDTO.builder().domainOrIp(domainOrIp).authTypeCode(authTypeEnum.getCode())
.build());
return OAuth2ClientRegistrationAdapter.getClientRegistration(authClientDTO.getId().toString(),
authTypeEnum, authClientDTO);
}
}

View File

@@ -0,0 +1,109 @@
package com.newyetai.security.social.service;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequestEntityConverter;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
import com.newyetai.security.social.util.Oauth2ProviderUtils;
public class StandardOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
private static final String MISSING_USER_INFO_URI_ERROR_CODE = "missing_user_info_uri";
private static final String MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE =
"missing_user_name_attribute";
private static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = "invalid_user_info_response";
private Converter<OAuth2UserRequest, RequestEntity<?>> requestEntityConverter =
new OAuth2UserRequestEntityConverter();
private RestOperations restOperations;
public StandardOAuth2UserService() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
this.restOperations = restTemplate;
}
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
Assert.notNull(userRequest, "userRequest cannot be null");
if (!StringUtils.hasText(
userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri())) {
OAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_INFO_URI_ERROR_CODE,
"Missing required UserInfo Uri in UserInfoEndpoint for Client Registration: "
+ userRequest.getClientRegistration().getRegistrationId(),
null);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails()
.getUserInfoEndpoint().getUserNameAttributeName();
if (!StringUtils.hasText(userNameAttributeName)) {
OAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE,
"Missing required \"user name\" attribute name in UserInfoEndpoint for Client Registration: "
+ userRequest.getClientRegistration().getRegistrationId(),
null);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
IOauthProvider provider = Oauth2ProviderUtils
.getProvider(userRequest.getClientRegistration().getClientName());
// 不同类型不同请求体
Converter<OAuth2UserRequest, RequestEntity<?>> converter =
provider.getUserConvert();
if (converter == null) {
converter = requestEntityConverter;
}
// 获得request
RequestEntity<?> request = converter.convert(userRequest);
ResponseEntity<String> response;
try {
// 执行request
response = this.restOperations.exchange(request, String.class);
} catch (OAuth2AuthorizationException ex) {
OAuth2Error oauth2Error = ex.getError();
StringBuilder errorDetails = new StringBuilder();
errorDetails.append("Error details: [");
errorDetails.append("UserInfo Uri: ").append(
userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri());
errorDetails.append(", Error Code: ").append(oauth2Error.getErrorCode());
if (oauth2Error.getDescription() != null) {
errorDetails.append(", Error Description: ").append(oauth2Error.getDescription());
}
errorDetails.append("]");
oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,
"An error occurred while attempting to retrieve the UserInfo Resource: "
+ errorDetails.toString(),
null);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
} catch (RestClientException ex) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,
"An error occurred while attempting to retrieve the UserInfo Resource: "
+ ex.getMessage(),
null);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
}
// 解析response
String userAttributes = response.getBody();
return provider.dealUser(userRequest, userAttributes);
}
}

View File

@@ -0,0 +1,189 @@
package com.newyetai.security.social.service;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
import org.springframework.security.crypto.keygen.StringKeyGenerator;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.util.UriComponentsBuilder;
import com.newyetai.common.util.RequestUtils;
import com.newyetai.security.social.util.Oauth2ProviderUtils;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.user.constant.UserTypeEnum;
/**
* An implementation of an {@link OAuth2AuthorizationRequestResolver} that attempts to
* resolve an {@link OAuth2AuthorizationRequest} from the provided {@code HttpServletRequest}
* using the default request {@code URI} pattern {@code /oauth2/authorization/{registrationId}}.
*
* <p>
* <b>NOTE:</b> The default base {@code URI} {@code /oauth2/authorization} may be overridden
* via it's constructor {@link #DefaultOAuth2AuthorizationRequestResolver(ClientRegistrationRepository, String)}.
*
* @author Joe Grandja
* @author Rob Winch
* @since 5.1
* @see OAuth2AuthorizationRequestResolver
* @see OAuth2AuthorizationRequestRedirectFilter
*/
public final class StandardOauth2AuthorizationRequestResolver
implements OAuth2AuthorizationRequestResolver {
private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId";
private final ClientRegistrationRepository clientRegistrationRepository;
private final AntPathRequestMatcher authorizationRequestMatcher;
private final StringKeyGenerator stateGenerator =
new Base64StringKeyGenerator(Base64.getUrlEncoder());
/**
* Constructs a {@code DefaultOAuth2AuthorizationRequestResolver} using the provided parameters.
*
* @param clientRegistrationRepository the repository of client registrations
* @param authorizationRequestBaseUri the base {@code URI} used for resolving authorization requests
*/
public StandardOauth2AuthorizationRequestResolver(
// StandardClientRegistrationRepository standardClientRegistrationRepository,
ClientRegistrationRepository standardClientRegistrationRepository,
String authorizationRequestBaseUri) {
Assert.notNull(standardClientRegistrationRepository,
"clientRegistrationRepository cannot be null");
Assert.hasText(authorizationRequestBaseUri, "authorizationRequestBaseUri cannot be empty");
this.clientRegistrationRepository = standardClientRegistrationRepository;
this.authorizationRequestMatcher = new AntPathRequestMatcher(
authorizationRequestBaseUri + "/{" + REGISTRATION_ID_URI_VARIABLE_NAME + "}");
}
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
String registrationId = this.resolveRegistrationId(request);
String redirectUriAction = getAction(request, "login");
return resolve(request, registrationId, redirectUriAction);
}
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId) {
if (registrationId == null) {
return null;
}
String redirectUriAction = getAction(request, "authorize");
return resolve(request, registrationId, redirectUriAction);
}
private String getAction(HttpServletRequest request, String defaultAction) {
String action = request.getParameter("action");
if (action == null) {
return defaultAction;
}
return action;
}
private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId,
String redirectUriAction) {
if (registrationId == null) {
return null;
}
String domainOrIp = RequestUtils.getDomain(request);
ClientRegistration clientRegistration = null;
if (clientRegistrationRepository instanceof StandardClientRegistrationRepository) {
clientRegistration =
((StandardClientRegistrationRepository) this.clientRegistrationRepository)
.findByRegistrationId(registrationId, domainOrIp);
} else {
clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
}
if (clientRegistration == null) {
throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
}
OAuth2AuthorizationRequest.Builder builder;
if (AuthorizationGrantType.AUTHORIZATION_CODE
.equals(clientRegistration.getAuthorizationGrantType())) {
builder = OAuth2AuthorizationRequest.authorizationCode();
} else if (AuthorizationGrantType.IMPLICIT
.equals(clientRegistration.getAuthorizationGrantType())) {
builder = OAuth2AuthorizationRequest.implicit();
} else {
throw new IllegalArgumentException("Invalid Authorization Grant Type (" +
clientRegistration.getAuthorizationGrantType().getValue() +
") for Client Registration with Id: " + clientRegistration.getRegistrationId());
}
String redirectUriStr = this.expandRedirectUri(request, clientRegistration, redirectUriAction);
Map<String, Object> additionalParameters = new HashMap<>();
additionalParameters.put(OAuth2ParameterNames.REGISTRATION_ID,
clientRegistration.getRegistrationId());
String userType = request.getParameter(SecurityConstants.LOGIN_USER_TYPE_KEY);
String registerCode = request.getParameter(SecurityConstants.LOGIN_REGISTER_CODE_KEY);
String entrypoint = request.getParameter(SecurityConstants.LOGIN_ENTRYPOINT_KEY);
if (StringUtils.isBlank(userType)) {
userType = UserTypeEnum.PERSON.getType().toString();
}
additionalParameters.put(SecurityConstants.LOGIN_USER_TYPE_KEY, userType);
if (StringUtils.isNotBlank(registerCode)) {
additionalParameters.put(SecurityConstants.LOGIN_REGISTER_CODE_KEY, registerCode);
}
if (StringUtils.isNotBlank(entrypoint)) {
additionalParameters.put(SecurityConstants.LOGIN_ENTRYPOINT_KEY, entrypoint);
}
OAuth2AuthorizationRequest authorizationRequest = builder
.clientId(clientRegistration.getClientId())
.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
.redirectUri(redirectUriStr)
.scopes(clientRegistration.getScopes())
.state(this.stateGenerator.generateKey())
.additionalParameters(additionalParameters)
.build();
// 不同授权需要额外修饰
IOauthProvider oauthProvider = Oauth2ProviderUtils.getProvider(registrationId);
if (oauthProvider != null) {
return oauthProvider.decorateRequest(request, authorizationRequest);
}
return authorizationRequest;
}
private String resolveRegistrationId(HttpServletRequest request) {
if (this.authorizationRequestMatcher.matches(request)) {
return this.authorizationRequestMatcher
.extractUriTemplateVariables(request).get(REGISTRATION_ID_URI_VARIABLE_NAME);
}
return null;
}
private String expandRedirectUri(HttpServletRequest request,
ClientRegistration clientRegistration, String action) {
// Supported URI variables -> baseUrl, action, registrationId
// Used in -> CommonOAuth2Provider.DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}"
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("registrationId", clientRegistration.getRegistrationId());
String baseUrl = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replaceQuery(null)
.replacePath(request.getContextPath())
.build()
.toUriString();
uriVariables.put("baseUrl", baseUrl);
if (action != null) {
uriVariables.put("action", action);
}
return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUriTemplate())
.buildAndExpand(uriVariables)
.toUriString();
}
}

View File

@@ -0,0 +1,67 @@
package com.newyetai.security.social.ssb.service;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.stereotype.Service;
import com.newyetai.sdk.ssb.SsbProperties;
import com.newyetai.sdk.ssb.util.SM2Utils;
import com.newyetai.security.social.service.IOauthProvider;
import com.newyetai.security.util.SecurityUtils;
import com.newyetai.usercenter.auth.constant.AuthTypeEnum;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class SsbOauthLoginProvider implements IOauthProvider {
private static final String AUTH_URL =
"https://api.eshimin.com/newOauth/authorize?client_id=%s&response_type=authorization_code&scope=read&redirect_uri=%s&timestamp=%s&sign=%s&state=%s";
@Autowired
private SsbProperties ssbProperties;
@Override
public String getName() {
return AuthTypeEnum.SSB_APP.name();
}
@Override
public OAuth2AuthorizationRequest decorateRequest(HttpServletRequest request,
OAuth2AuthorizationRequest authorizationRequest) {
String timestamp = String.valueOf(System.currentTimeMillis());
String sign = SM2Utils.sign(ssbProperties.getSm2PrivateKey(),
ssbProperties.getAppKey() + ssbProperties.getAppSecret() + timestamp,
ssbProperties.getAppKey());
// 指定成功跳转地址
String redirectUrl =
SecurityUtils.dealRedirectUrl(request, authorizationRequest.getRedirectUri());
String state = "123";
// 微信Authorization Request URL
String authorizationRequestUri =
String.format(AUTH_URL,
authorizationRequest.getClientId(),
SecurityUtils.encodeURL(redirectUrl),
timestamp,
sign,
state);
return OAuth2AuthorizationRequest.from(authorizationRequest)
.authorizationRequestUri(authorizationRequestUri)
.build();
}
}

View File

@@ -0,0 +1,26 @@
package com.newyetai.security.social.util;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.map.CaseInsensitiveMap;
import org.springframework.stereotype.Component;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.security.social.service.IOauthProvider;
@Component
public class Oauth2ProviderUtils {
private static Map<String, IOauthProvider> providerMap = new CaseInsensitiveMap<String, IOauthProvider>();
private Oauth2ProviderUtils(List<IOauthProvider> providers) {
providers.forEach(provider -> providerMap.put(provider.getName(), provider));
}
public static IOauthProvider getProvider(String providerName) {
IOauthProvider provider = providerMap.get(providerName);
return provider;
}
}

View File

@@ -0,0 +1,20 @@
package com.newyetai.security.social.wechat.constant;
/**
* @author sr on 2021-01-13
*/
public interface WechatConstants {
/** 授权code获取token **/
String TOKEN_URL_FORMAT =
"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=%s";
/** openId换取用户信息 **/
String USER_URL_FORMAT =
"https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=%s";
/** 调起微信授权地址 **/
String AUTH_URL_FORMAT =
"https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=%s&scope=%s&state=%s#wechat_redirect";
}

View File

@@ -0,0 +1,57 @@
package com.newyetai.security.social.wechat.convert;
import java.net.URI;
import java.util.Collections;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.web.util.UriComponentsBuilder;
import com.newyetai.security.social.wechat.constant.WechatConstants;
import lombok.extern.slf4j.Slf4j;
/**
* code换token
*
* @author sr on 2021-01-12
*/
@Slf4j
public class WeChatAuthorizationCodeGrantRequestEntityConverter
implements Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> {
@Override
public RequestEntity<?> convert(
OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
HttpHeaders headers = getTokenRequestHeaders();
URI uri = buildUri(authorizationCodeGrantRequest);
return new RequestEntity<>(headers, HttpMethod.GET, uri);
}
private HttpHeaders getTokenRequestHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.TEXT_PLAIN));
return headers;
}
private URI buildUri(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration();
String tokenUri = clientRegistration.getProviderDetails().getTokenUri();
String appid = clientRegistration.getClientId();
String secret = clientRegistration.getClientSecret();
String code = authorizationCodeGrantRequest.getAuthorizationExchange()
.getAuthorizationResponse().getCode();
String grantType = authorizationCodeGrantRequest.getGrantType().getValue();
String uriString =
String.format(WechatConstants.TOKEN_URL_FORMAT,
appid, secret, code, grantType);
return UriComponentsBuilder.fromUriString(uriString).build().toUri();
}
}

View File

@@ -0,0 +1,44 @@
package com.newyetai.security.social.wechat.convert;
import java.net.URI;
import java.util.Collections;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.web.util.UriComponentsBuilder;
import com.newyetai.security.social.wechat.constant.WechatConstants;
public class WeChatUserRequestEntityConverter
implements Converter<OAuth2UserRequest, RequestEntity<?>> {
@Override
public RequestEntity<?> convert(OAuth2UserRequest userRequest) {
HttpHeaders headers = getUserRequestHeaders();
URI uri = buildUri(userRequest);
return new RequestEntity<>(headers, HttpMethod.GET, uri);
}
private HttpHeaders getUserRequestHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.TEXT_PLAIN));
return headers;
}
private URI buildUri(OAuth2UserRequest userRequest) {
ClientRegistration clientRegistration = userRequest.getClientRegistration();
String uri = clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri();
String accessToken = userRequest.getAccessToken().getTokenValue();
String openId = (String) userRequest.getAdditionalParameters().get("openid");
String userInfoUrl =
String.format(WechatConstants.USER_URL_FORMAT,
accessToken, openId, "zh_CN");
return UriComponentsBuilder.fromUriString(userInfoUrl).build().toUri();
}
}

View File

@@ -0,0 +1,70 @@
package com.newyetai.security.social.wechat.service;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.newyetai.common.util.JsonUtils;
import com.newyetai.security.social.service.IOauthProvider;
import com.newyetai.usercenter.constants.SecurityConstants;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Getter
@Setter
@Slf4j
public class WeChatAccessTokenResponse {
@JsonProperty("access_token")
private String accessToken;
@JsonProperty("expires_in")
private Long expiresIn;
@JsonProperty("refresh_token")
private String refreshToken;
private String openid;
private String scope;
private WeChatAccessTokenResponse() {
}
public static WeChatAccessTokenResponse build(String json) {
try {
return JsonUtils.toObject(json, WeChatAccessTokenResponse.class);
} catch (Exception e) {
log.error("An error occurred while attempting to parse the WeiXin Access Token Response: "
+ e.getMessage());
return null;
}
}
public OAuth2AccessTokenResponse toOAuth2AccessTokenResponse(
OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken);
builder.tokenType(OAuth2AccessToken.TokenType.BEARER);
builder.expiresIn(expiresIn);
builder.refreshToken(refreshToken);
String[] scopes = scope.split(",");
Set<String> scopeSet = new HashSet<>();
Collections.addAll(scopeSet, scopes);
builder.scopes(scopeSet);
Map<String, Object> additionalParameters = new LinkedHashMap<>();
additionalParameters.put("openid", openid);
Map<String, Object> originParam = authorizationCodeGrantRequest.getAuthorizationExchange().getAuthorizationRequest()
.getAdditionalParameters();
additionalParameters.putAll(originParam);
builder.additionalParameters(additionalParameters);
return builder.build();
}
}

View File

@@ -0,0 +1,157 @@
package com.newyetai.security.social.wechat.service;
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.client.RestClientException;
import com.newyetai.common.util.JsonUtils;
import com.newyetai.security.mapstruct.AuthDetailMapMapper;
import com.newyetai.security.mapstruct.UserMapMapper;
import com.newyetai.security.social.service.IOauthProvider;
import com.newyetai.security.social.service.StandardAuthorizationCodeTokenResponseClient;
import com.newyetai.security.social.wechat.constant.WechatConstants;
import com.newyetai.security.social.wechat.convert.WeChatAuthorizationCodeGrantRequestEntityConverter;
import com.newyetai.security.social.wechat.convert.WeChatUserRequestEntityConverter;
import com.newyetai.security.util.SecurityUtils;
import com.newyetai.usercenter.auth.constant.AuthTypeEnum;
import com.newyetai.usercenter.auth.dto.AuthDetailLoginDTO;
import com.newyetai.usercenter.auth.wechat.dto.WechatAuthUserInfoDTO;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
import com.newyetai.usercenter.user.login.service.UserLoginService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class WechatOauthLoginProvider implements IOauthProvider {
private Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> tokenConverter =
new WeChatAuthorizationCodeGrantRequestEntityConverter();
private Converter<OAuth2UserRequest, RequestEntity<?>> userConverter =
new WeChatUserRequestEntityConverter();
private UserLoginService userLoginService;
public WechatOauthLoginProvider(UserLoginService userLoginService) {
this.userLoginService = userLoginService;
}
@Override
public String getName() {
return AuthTypeEnum.WECHAT.name();
}
@Override
public OAuth2AuthorizationRequest decorateRequest(HttpServletRequest request,
OAuth2AuthorizationRequest authorizationRequest) {
// 指定成功跳转地址
String redirectUrl =
SecurityUtils.dealRedirectUrl(request, authorizationRequest.getRedirectUri());
// 微信Authorization Request URL
String authorizationRequestUri =
String.format(WechatConstants.AUTH_URL_FORMAT,
authorizationRequest.getClientId(),
SecurityUtils.encodeURL(redirectUrl),
OAuth2AuthorizationResponseType.CODE.getValue(), getScope(authorizationRequest),
authorizationRequest.getState());
return OAuth2AuthorizationRequest.from(authorizationRequest)
.authorizationRequestUri(authorizationRequestUri)
.build();
}
private static String getScope(OAuth2AuthorizationRequest authorizationRequest) {
return authorizationRequest.getScopes().stream().findFirst().orElse("snsapi_userinfo");
}
@Override
public OAuth2AccessTokenResponse getTokenResponse(
OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
// 调用WeChatAuthorizationCodeGrantRequestEntityConverter获取request
RequestEntity<?> request = this.tokenConverter.convert(authorizationCodeGrantRequest);
ResponseEntity<String> response;
try {
// 执行request
response = StandardAuthorizationCodeTokenResponseClient.restOperations.exchange(request,
String.class);
} catch (RestClientException ex) {
String description =
"An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: ";
log.error(description, ex);
OAuth2Error oauth2Error =
new OAuth2Error(
StandardAuthorizationCodeTokenResponseClient.INVALID_TOKEN_RESPONSE_ERROR_CODE,
description + ex.getMessage(), null);
throw new OAuth2AuthorizationException(oauth2Error, ex);
}
// 解析response
OAuth2AccessTokenResponse tokenResponse =
WeChatAccessTokenResponse.build(response.getBody()).toOAuth2AccessTokenResponse(authorizationCodeGrantRequest);
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {
tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse)
.scopes(authorizationCodeGrantRequest.getClientRegistration().getScopes())
.build();
}
return tokenResponse;
}
@Override
public Converter<OAuth2UserRequest, RequestEntity<?>> getUserConvert() {
return userConverter;
}
@Override
public OAuth2User dealUser(OAuth2UserRequest userRequest, String userJsonStr) {
WechatAuthUserInfoDTO wechatUserInfoDTO = null;
try {
// 编码转换
userJsonStr = new String(userJsonStr.getBytes("ISO-8859-1"), "UTF-8");
wechatUserInfoDTO = JsonUtils.toObject(userJsonStr, WechatAuthUserInfoDTO.class);
} catch (UnsupportedEncodingException e) {
log.error("An error occurred while attempting to encode userAttributes: " + e.getMessage());
}
AuthDetailLoginDTO authDetailLoginDTO = AuthDetailMapMapper.INSTANCE.toDto(wechatUserInfoDTO);
Integer userType = MapUtils.getInteger(userRequest.getAdditionalParameters(),
SecurityConstants.LOGIN_USER_TYPE_KEY);
String registerCode = MapUtils.getString(userRequest.getAdditionalParameters(),
SecurityConstants.LOGIN_REGISTER_CODE_KEY);
String entrypoint = MapUtils.getString(userRequest.getAdditionalParameters(),
SecurityConstants.LOGIN_ENTRYPOINT_KEY);
authDetailLoginDTO
.setUserType(userType);
authDetailLoginDTO
.setAuthClientId(Long.valueOf(userRequest.getClientRegistration().getRegistrationId()));
authDetailLoginDTO.setAuthTypeCode(AuthTypeEnum.WECHAT.getCode());
authDetailLoginDTO.setRegisterCode(registerCode);
authDetailLoginDTO.setEntrypoint(entrypoint);
UserDetailDTO userDetailDTO = userLoginService.findUserDetailDTO(authDetailLoginDTO);
return UserMapMapper.INSTANCE.dtoToDto(userDetailDTO);
}
}

View File

@@ -0,0 +1,70 @@
package com.newyetai.security.ssb;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.newyetai.common.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
/**
* 账号密码认证过滤器,指定请求组装token,调起认证
*
* @author sr on 2021-06-21
*/
@Slf4j
public class SsbAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private boolean postOnly = true;
public static final String REQUEST_METHOD = "GET";
public static final String LOGIN_URI = "/h5/ssb_login_notify";
public static final AntPathRequestMatcher requiresAuthenticationRequestMatcher =
new AntPathRequestMatcher(LOGIN_URI, REQUEST_METHOD);
public static final String PARAM_PLATFORM = "platform";
public static final String PARAM_ACCESS_TOKEN = "access_token";
public static final String PARAM_CODE = "code";
public static final String PARAM_STATE = "state";
public SsbAuthenticationFilter() {
super(requiresAuthenticationRequestMatcher);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !REQUEST_METHOD.equals(request.getMethod())) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String platform = request.getParameter(PARAM_PLATFORM);
String accessToken = request.getParameter(PARAM_ACCESS_TOKEN);
String code = request.getParameter(PARAM_CODE);
String state = request.getParameter(PARAM_STATE);
log.info("ssb login req params:{}", JsonUtils.toJsonString(request.getParameterMap()));
AbstractAuthenticationToken authRequest = new SsbAuthenticationToken(
SsbLoginDTO.builder().accessToken(accessToken).platform(platform).code(code).state(state).build(), accessToken);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}

View File

@@ -0,0 +1,71 @@
package com.newyetai.security.ssb;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.common.util.JsonUtils;
import com.newyetai.common.validation.util.ValidateUtils;
import com.newyetai.security.dto.SecurityUserDTO;
import com.newyetai.security.expection.BadLoginException;
import com.newyetai.security.mapstruct.UserMapMapper;
import com.newyetai.usercenter.user.login.dto.LoginUsernamePasswordDTO;
import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
import com.newyetai.usercenter.user.login.service.UserLoginService;
import lombok.extern.slf4j.Slf4j;
/**
* 账号密码认证provider,比对信息是否匹配
*
* @author sr on 2021-06-21
*/
@Slf4j
public class SsbAuthenticationProvider implements AuthenticationProvider {
private UserLoginService userLoginService;
public SsbAuthenticationProvider(UserLoginService userLoginService) {
this.userLoginService = userLoginService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SsbLoginDTO ssbLoginDTO =
(SsbLoginDTO) authentication.getPrincipal();
log.info("ssb login req:{}", JsonUtils.toJsonString(ssbLoginDTO));
String errMsg = ValidateUtils.validate(ssbLoginDTO);
Assert.isTrue(errMsg == null, new BadLoginException(errMsg));
SecurityUserDTO loadedUser = null;
try {
UserDetailDTO userDetailDTO = this.userLoginService.loadUser(ssbLoginDTO);
loadedUser = UserMapMapper.INSTANCE.dtoToDto(userDetailDTO);
} catch (BadLoginException be) {
throw be;
} catch (Exception e) {
throw new BadLoginException(e.getMessage(), e);
}
SsbAuthenticationToken result =
new SsbAuthenticationToken(loadedUser, listUserGrantedAuthorities(loadedUser));
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return (SsbAuthenticationToken.class.isAssignableFrom(authentication));
}
private Set<GrantedAuthority> listUserGrantedAuthorities(UserDetails userDetails) {
// 什么都不用做,访问资源鉴权的时会拿userDetail去缓存获取对应权限
return new HashSet<GrantedAuthority>();
}
}

View File

@@ -0,0 +1,25 @@
package com.newyetai.security.ssb;
import java.util.Collection;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
/**
* 账号密码认证信息
* @author sr on 2021-06-21
*/
public class SsbAuthenticationToken extends UsernamePasswordAuthenticationToken {
public SsbAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public SsbAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}

View File

@@ -0,0 +1,34 @@
package com.newyetai.security.ssb;
import java.io.Serializable;
import javax.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SsbLoginDTO implements Serializable {
/** **/
private static final long serialVersionUID = 6780283569028235063L;
/** 平台标识
eshimin: 随申办 APP
weixinmini:微信小程序
weixinmp:微信公众号
alipaymini:支付宝小程序
alipayfuwu:支付宝生活号 **/
// @NotBlank(message = "平台标识不能为空")
private String platform;
/** 用户令牌,作为获取用户信息的入参 **/
private String accessToken;
/** oauth code **/
private String code;
private String state;
}

View File

@@ -0,0 +1,100 @@
package com.newyetai.security.temptoken;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.newyetai.security.expection.BadLoginException;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.user.login.dto.LoginPhoneToken;
import com.newyetai.usercenter.user.login.dto.LoginUserToken;
import com.newyetai.usercenter.user.login.dto.TempTokenDTO;
/**
* 临时token认证过滤器,指定请求组装token,调起认证
*
* @author sr on 2020-06-04
*/
public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String TYPE_KEY = "type";
public static final String REQUEST_METHOD = "POST";
private static final AntPathRequestMatcher requiresAuthenticationRequestMatcher =
new AntPathRequestMatcher("/login_token_" + "{" + TYPE_KEY + "}", REQUEST_METHOD);
private boolean postOnly = true;
public TokenAuthenticationFilter() {
super(requiresAuthenticationRequestMatcher);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !REQUEST_METHOD.equals(request.getMethod())) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String type = this.resolveType(request);
String tempToken = request.getParameter(SecurityConstants.LOGIN_TEMP_TOKEN_KEY);
String entrypoint = request.getParameter(SecurityConstants.LOGIN_ENTRYPOINT_KEY);
TempTokenDTO tempTokenDTO = null;
if (TempTokenDTO.TYPE_PHONE.equals(type)) {
Long userId = null;
String userIdStr = request.getParameter(SecurityConstants.LOGIN_USER_ID_KEY);
if (StringUtils.isNotBlank(userIdStr)) {
try {
userId = Long.valueOf(userIdStr);
} catch (NumberFormatException e) {
throw new BadLoginException("用户id错误:" + userIdStr);
}
}
tempTokenDTO = LoginPhoneToken.builder().tempToken(tempToken).entrypoint(entrypoint)
.userId(userId).build();
} else if (TempTokenDTO.TYPE_USER.equals(type)) {
Long orgId = null;
String orgIdStr = request.getParameter(SecurityConstants.LOGIN_ORG_ID_KEY);
try {
orgId = Long.valueOf(orgIdStr);
} catch (NumberFormatException e) {
throw new BadLoginException("机构id错误:" + orgIdStr);
}
tempTokenDTO = LoginUserToken.builder().tempToken(tempToken).orgId(orgId).build();
} else {
throw new BadLoginException("token认证类型有误");
}
AbstractAuthenticationToken authRequest = new TokenAuthenticationToken(tempTokenDTO, tempToken);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
private String resolveType(HttpServletRequest request) {
if (requiresAuthenticationRequestMatcher.matches(request)) {
return requiresAuthenticationRequestMatcher.extractUriTemplateVariables(request)
.get(TYPE_KEY);
}
return null;
}
}

View File

@@ -0,0 +1,74 @@
package com.newyetai.security.temptoken;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.common.validation.util.ValidateUtils;
import com.newyetai.security.dto.SecurityUserDTO;
import com.newyetai.security.expection.BadLoginException;
import com.newyetai.security.mapstruct.UserMapMapper;
import com.newyetai.usercenter.user.login.dto.LoginPhoneToken;
import com.newyetai.usercenter.user.login.dto.LoginUserToken;
import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
import com.newyetai.usercenter.user.login.service.UserLoginService;
/**
* token认证provider,比对信息是否匹配
*
* @author sr on 2020-06-04
*/
public class TokenAuthenticationProvider implements AuthenticationProvider {
private UserLoginService userLoginService;
public TokenAuthenticationProvider(UserLoginService userLoginService) {
this.userLoginService = userLoginService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Object principal = authentication.getPrincipal();
String errMsg = ValidateUtils.validate(principal);
Assert.isTrue(errMsg == null, new BadLoginException(errMsg));
SecurityUserDTO loadedUser = null;
try {
UserDetailDTO userDetailDTO = null;
if (principal instanceof LoginPhoneToken) {
userDetailDTO = userLoginService.loadUser((LoginPhoneToken) principal);
} else if (principal instanceof LoginUserToken) {
userDetailDTO = userLoginService.loadUser((LoginUserToken) principal);
}else {
throw new BadLoginException("token认证类型有误");
}
loadedUser = UserMapMapper.INSTANCE.dtoToDto(userDetailDTO);
} catch (BadLoginException be) {
throw be;
} catch (Exception e) {
throw new BadLoginException(e.getMessage(), e);
}
TokenAuthenticationToken result =
new TokenAuthenticationToken(loadedUser, listUserGrantedAuthorities(loadedUser));
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return (TokenAuthenticationToken.class.isAssignableFrom(authentication));
}
private Set<GrantedAuthority> listUserGrantedAuthorities(UserDetails userDetails) {
// 什么都不用做,访问资源鉴权的时会拿userDetail去缓存获取对应权限
return new HashSet<GrantedAuthority>();
}
}

View File

@@ -0,0 +1,24 @@
package com.newyetai.security.temptoken;
import java.util.Collection;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
/**
* 临时token认证信息
*
* @author sr on 2020-06-04
*/
public class TokenAuthenticationToken extends UsernamePasswordAuthenticationToken {
public TokenAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public TokenAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}

View File

@@ -0,0 +1,25 @@
package com.newyetai.security.temptoken.dto;
import java.util.List;
import com.newyetai.usercenter.user.login.dto.PhoneToken;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@NoArgsConstructor
@SuperBuilder
@ApiModel(description = "多用户手机号token")
public class DuplMobileToken extends PhoneToken{
/** **/
private static final long serialVersionUID = -4119001378908265360L;
@ApiModelProperty("用户列表")
private List<?> users;
}

View File

@@ -0,0 +1,25 @@
package com.newyetai.security.temptoken.dto;
import java.util.List;
import com.newyetai.usercenter.user.login.dto.UserToken;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@NoArgsConstructor
@SuperBuilder
@ApiModel(description = "多机构用户token")
public class DuplUserToken extends UserToken{
/** **/
private static final long serialVersionUID = -374663184680420441L;
@ApiModelProperty("机构列表")
private List<?> orgs;
}

View File

@@ -0,0 +1,20 @@
package com.newyetai.security.temptoken.dto;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PhoneTokenCreateDTO implements Serializable{
private Integer userType;
private String phone;
}

View File

@@ -0,0 +1,19 @@
package com.newyetai.security.temptoken.dto;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserTokenCreateDTO implements Serializable{
private Integer userType;
private Long userId;
}

View File

@@ -0,0 +1,71 @@
package com.newyetai.security.upassword;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.newyetai.security.expection.BadLoginException;
import com.newyetai.usercenter.constants.SecurityConstants;
import com.newyetai.usercenter.user.login.dto.LoginUsernamePasswordDTO;
/**
* 账号密码认证过滤器,指定请求组装token,调起认证
*
* @author sr on 2021-06-21
*/
public class UpasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private boolean postOnly = true;
public static final String REQUEST_METHOD = "POST";
public static final AntPathRequestMatcher requiresAuthenticationRequestMatcher =
new AntPathRequestMatcher("/login", REQUEST_METHOD);
public UpasswordAuthenticationFilter() {
super(requiresAuthenticationRequestMatcher);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !REQUEST_METHOD.equals(request.getMethod())) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String userTypeStr = request.getParameter(SecurityConstants.LOGIN_USER_TYPE_KEY);
Integer userType = null;
try {
userType = Integer.valueOf(userTypeStr);
} catch (NumberFormatException e) {
throw new BadLoginException("用户类型错误:" + userTypeStr);
}
String username = request.getParameter(SecurityConstants.LOGIN_USERNAME_KEY);
String password = request.getParameter(SecurityConstants.LOGIN_PASSWORD_KEY);
username = username == null ? "" : username.trim();
password = password == null ? "" : password.trim();
AbstractAuthenticationToken authRequest = new UpasswordAuthenticationToken(
LoginUsernamePasswordDTO.builder().username(username).password(password).userType(userType).build(), password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}

View File

@@ -0,0 +1,66 @@
package com.newyetai.security.upassword;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.common.validation.util.ValidateUtils;
import com.newyetai.security.dto.SecurityUserDTO;
import com.newyetai.security.expection.BadLoginException;
import com.newyetai.security.mapstruct.UserMapMapper;
import com.newyetai.usercenter.user.login.dto.LoginUsernamePasswordDTO;
import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
import com.newyetai.usercenter.user.login.service.UserLoginService;
/**
* 账号密码认证provider,比对信息是否匹配
*
* @author sr on 2021-06-21
*/
public class UpasswordAuthenticationProvider implements AuthenticationProvider {
private UserLoginService userLoginService;
public UpasswordAuthenticationProvider(UserLoginService userLoginService) {
this.userLoginService = userLoginService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
LoginUsernamePasswordDTO loginUsernamePasswordDTO =
(LoginUsernamePasswordDTO) authentication.getPrincipal();
String errMsg = ValidateUtils.validate(loginUsernamePasswordDTO);
Assert.isTrue(errMsg == null, new BadLoginException(errMsg));
SecurityUserDTO loadedUser = null;
try {
UserDetailDTO userDetailDTO = this.userLoginService.loadUser(loginUsernamePasswordDTO);
loadedUser = UserMapMapper.INSTANCE.dtoToDto(userDetailDTO);
} catch (BadLoginException be) {
throw be;
} catch (Exception e) {
throw new BadLoginException(e.getMessage(), e);
}
UpasswordAuthenticationToken result =
new UpasswordAuthenticationToken(loadedUser, listUserGrantedAuthorities(loadedUser));
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return (UpasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
private Set<GrantedAuthority> listUserGrantedAuthorities(UserDetails userDetails) {
// 什么都不用做,访问资源鉴权的时会拿userDetail去缓存获取对应权限
return new HashSet<GrantedAuthority>();
}
}

View File

@@ -0,0 +1,25 @@
package com.newyetai.security.upassword;
import java.util.Collection;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
/**
* 账号密码认证信息
* @author sr on 2021-06-21
*/
public class UpasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
public UpasswordAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public UpasswordAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}

View File

@@ -0,0 +1,35 @@
package com.newyetai.security.util;
/**
* request请求host上下文
* @author sr on 2021-08-23
*/
public class HostContextHolder {
private static final ThreadLocal<String> HOST = new ThreadLocal<String>();
/**
*
* @author sr on 2021-08-18
*/
public static void clearHost() {
HOST.remove();
}
/**
* @param host
* @author sr on 2021-08-18
*/
public static void setHost(String host) {
HOST.set(host);
}
/**
* @param host
* @author sr on 2021-08-18
*/
public static String getHost() {
return HOST.get();
}
}

View File

@@ -0,0 +1,68 @@
package com.newyetai.security.util;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.common.util.UserContextHolder;
import com.newyetai.security.dto.SecurityUserDTO;
import com.newyetai.usercenter.constants.OrgConstants;
import com.newyetai.usercenter.constants.UserConstants;
import com.newyetai.usercenter.user.constant.UserTypeEnum;
import com.newyetai.usercenter.user.dto.UserDetailQueryDTO;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* @author sr on 2020-01-05
*/
public class MockUtils {
/**
* 模拟登录机构用户,默认超级管理员/平台机构
*/
public static void mockPlatUser() {
mockUser(UserConstants.SUPERIOR_USER_ID, OrgConstants.PLATFORM_ORG_ID,UserTypeEnum.ORG.getType());
}
/**
* 模拟登录用户
*
* @param userId
* @param orgId(企业用户需要,个人不用可不传)
* @author sr on 2020-09-07
*/
public static void mockUser(Long userId, Long orgId) {
mockUser(userId, orgId, null);
}
/**
* 模拟登录用户(能拿到外部线程登录用户信息,则直接用外部的,不要自己定义固定值)
* 使用mockUser(userId,orgId), 不需要userType,userId已经有用户类型了
* @param userId
* @param orgId 个人用户可为空
* @param userType {@link UserTypeEnum}
* @author sr on 2020-09-07
*/
@Deprecated
public static void mockUser(Long userId, Long orgId,Integer userType) {
Assert.notNull(userId, "无userId,无法mock..");
UserDetailQueryDTO userDetailQueryDto =
UserDetailQueryDTO.builder().userId(userId).orgId(orgId).userType(userType).build();
SecurityUserDTO securityUserDTO = UserLoginUtils.loadUserToMock(userDetailQueryDto);
SecurityContextHolder.getContext()
.setAuthentication(new UsernamePasswordAuthenticationToken(
securityUserDTO, null));
UserContextHolder.setContext(securityUserDTO);
}
/**
*
* @author sr on 2020-06-17
*/
public static void clearUser() {
SecurityContextHolder.clearContext();
UserContextHolder.clearContext();
}
}

View File

@@ -0,0 +1,25 @@
package com.newyetai.security.util;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class PasswordUtils {
private static PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
private PasswordUtils(PasswordEncoder passwordEncoder) {
PasswordUtils.passwordEncoder = passwordEncoder;
}
public static String encode(String password) {
return passwordEncoder.encode(password);
}
public static boolean match(String password, String encodedPassword) {
return passwordEncoder.matches(password, encodedPassword);
}
}

View File

@@ -0,0 +1,25 @@
package com.newyetai.security.util;
import org.springframework.stereotype.Service;
import com.newyetai.common.service.IMockProvider;
@Service
public class SecurityMockProvider implements IMockProvider{
@Override
public void mockPlatUser() {
MockUtils.mockPlatUser();
}
@Override
public void mockUser(Long userId, Long orgId) {
MockUtils.mockUser(userId, orgId);
}
@Override
public void clearUser() {
MockUtils.clearUser();
}
}

View File

@@ -0,0 +1,119 @@
package com.newyetai.security.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import com.newyetai.common.util.EnvironmentUtils;
import com.newyetai.security.config.SecurityProperties;
import com.newyetai.usercenter.constants.SecurityConstants;
/**
* @author sr on 2020-09-10
*/
public abstract class SecurityPropertyUtils {
private static SecurityProperties securityProperties;
private static List<RequestMatcher> anonRequestMatchers;
private static RequestMatcher unacRequestMatcher = new AntPathRequestMatcher("/unac/**");
private static RequestMatcher authedRequestMatcher = new AntPathRequestMatcher("/authed/**");
public static void setSecurityProperties(SecurityProperties securityProperties) {
SecurityPropertyUtils.securityProperties = securityProperties;
}
public static SecurityProperties getProperties() {
return securityProperties;
}
/**
* 根据匹配路径获取请求匹配对象
*
* @param httpMethod
* @param antPatterns
* @return
* @author sr on 2020-09-10
*/
public static List<RequestMatcher> antMatchers(HttpMethod httpMethod,
String... antPatterns) {
String method = httpMethod == null ? null : httpMethod.toString();
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : antPatterns) {
matchers.add(new AntPathRequestMatcher(pattern, method));
}
return matchers;
}
/**
* 获取匿名配置匹配
*
* @return
* @author sr on 2020-09-10
*/
public static List<RequestMatcher> getAnonRequestMatchers() {
if (anonRequestMatchers != null) {
return anonRequestMatchers;
}
String anonymous = EnvironmentUtils.getProperty(SecurityConstants.PROP_KEY_SECURITY_ANONYMOUS);
if (StringUtils.isBlank(anonymous)) {
anonRequestMatchers = Collections.emptyList();
return anonRequestMatchers;
}
anonRequestMatchers = antMatchers(null, anonymous.split(","));
return anonRequestMatchers;
}
/**
* 判断请求是否匿名请求
*
* @param request
* @return
* @author sr on 2020-09-10
*/
public static boolean isAnon(HttpServletRequest request) {
List<RequestMatcher> matchers = getAnonRequestMatchers();
if (matchers.isEmpty()) {
return false;
}
for (RequestMatcher matcher : matchers) {
if (matcher.matches(request)) {
return true;
}
}
return false;
}
/**
* 判断请求是否未激活请求
*
* @param request
* @return
* @author sr on 2021-06-29
*/
public static boolean isUnac(HttpServletRequest request) {
return unacRequestMatcher.matches(request);
}
/**
* 判断请求是否已实名请求
* @param request
* @return
* @author sr on 2021-11-05
*/
public static boolean isAuthed(HttpServletRequest request) {
return authedRequestMatcher.matches(request);
}
}

View File

@@ -0,0 +1,146 @@
package com.newyetai.security.util;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import com.newyetai.common.constant.CommonConstants;
import com.newyetai.common.util.EnvironmentUtils;
import com.newyetai.common.util.RequestUtils;
import com.newyetai.usercenter.constants.SecurityConstants;
import cn.hutool.core.util.URLUtil;
import lombok.extern.slf4j.Slf4j;
/**
* @author sr on 2020-06-15
*/
@Slf4j
public class SecurityUtils {
/** 代理域名 **/
private static final String PROXY_DOMAIN = "proxy.niuyetai.cn";
/** 代理路径请求head-key **/
public static final String PROXY_REQUEST_HEADER_KEY = "X-Proxy-Uri";
public static String encodeURL(String url) {
try {
return URLEncoder.encode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.info("encode失败:{}", e.getMessage());
return null;
}
}
public static String decodeURL(String url) {
try {
if(url.contains("%")) {
return URLDecoder.decode(url, "UTF-8");
}
return url;
} catch (UnsupportedEncodingException e) {
log.info("encode失败:{}", e.getMessage());
return null;
}
}
/**
* 是否本地security调用 ;security.useLocal=false则关闭(调用户中心),其他都本地调用
*
* @return
* @author sr on 2020-06-15
*/
public static boolean useLocal() {
return !CommonConstants.FALSE_STR
.equals(EnvironmentUtils.getProperty(SecurityConstants.PROP_KEY_SECURITY_USELOCAL));
}
/**
* 处理代理路径
* @param request
* @param uri
* @return
* @author sr on 2021-01-16
*/
private static String dealProxy(HttpServletRequest request, String uri) {
String proxyUri = request.getHeader(PROXY_REQUEST_HEADER_KEY);
log.info("proxyUri {}", proxyUri);
// replace域名到代理域名
if (StringUtils.isNotBlank(proxyUri) && uri.contains(PROXY_DOMAIN)) {
return uri.replace(PROXY_DOMAIN, PROXY_DOMAIN + proxyUri);
}
return uri;
}
/**
* 指定成功跳转地址
*
* @param request
* @param redirectUri
* @return
* @author sr on 2021-01-12
*/
public static String dealRedirectUrl(HttpServletRequest request, final String redirectUri) {
String newRedirectUri = redirectUri;
String successToUrl = request.getParameter(SecurityConstants.SUCCESS_TO_URL);
if (StringUtils.isNotBlank(successToUrl)) {
newRedirectUri =
String.format(SecurityConstants.REDIRECT_URL_FORMAT, newRedirectUri,
encodeURL(successToUrl));
}
newRedirectUri = dealProxy(request, newRedirectUri);
log.info("newRedirectUri:{}", newRedirectUri);
return newRedirectUri;
}
public static String dealLoginUrl(HttpServletRequest request, String loginUri) {
String successToUrl = request.getParameter(SecurityConstants.SUCCESS_TO_URL);
// 指定成功跳转,但登陆地址未配置域名
if (StringUtils.isNotBlank(successToUrl)
&& loginUri.indexOf(RequestUtils.DOMAIN_SEPARATOR) < 0) {
String baseUrl = null;
try {
baseUrl = URLUtil.getHost(new URL(successToUrl)).toString();
} catch (MalformedURLException e) {
log.error("successToUrl get domain error:{}", e.getMessage());
return null;
}
loginUri = baseUrl + loginUri;
}
dealProxy(request, loginUri);
return loginUri;
}
public static String dealSuccessUrl(HttpServletRequest request, String successUri, String token) {
if (successUri == null) {
return null;
}
String redirectSuccessToUrl = request.getParameter(SecurityConstants.SUCCESS_TO_URL);
log.debug("redirectSuccessToUrl:{}", redirectSuccessToUrl);
successUri =
StringUtils.isBlank(redirectSuccessToUrl) ? successUri : decodeURL(redirectSuccessToUrl);
if (successUri.indexOf(RequestUtils.DOMAIN_SEPARATOR) > 0) {
String requestBaseUrl = RequestUtils.getHttpDomain(request);
// 成功地址与当前地址域名不一致,跨域,拼接token
if (!successUri.startsWith(requestBaseUrl)) {
if (successUri.indexOf("?") > 0) {
successUri += "&" + SecurityConstants.TOKEN + "=" + token;
} else {
successUri += "?" + SecurityConstants.TOKEN + "=" + token;
}
}
}
dealProxy(request, successUri);
log.info("last redirectSuccessToUrl:{}", successUri);
return successUri;
}
}

View File

@@ -0,0 +1,51 @@
package com.newyetai.security.util;
import org.springframework.stereotype.Component;
import com.newyetai.common.AlitaResult;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.security.dto.SecurityUserDTO;
import com.newyetai.security.mapstruct.UserMapMapper;
import com.newyetai.security.service.IUserSecurityService;
import com.newyetai.usercenter.user.dto.UserDetailQueryDTO;
import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
/**
* @author sr on 2020-06-15
*/
@Component
public class UserLoginUtils {
public static final String SPRING_SECURITY_FORM_ORG_ID_KEY = "orgId";
private static IUserSecurityService userService;
private UserLoginUtils(IUserSecurityService userService) {
UserLoginUtils.userService = userService;
}
/**
* 根据登录信息获取登录对象
*
* @param userCloudService
* @param userService
* @param userDetailQueryDto
* @return
* @author sr on 2020-06-15
*/
public static SecurityUserDTO loadUserToMock(UserDetailQueryDTO userDetailQueryDto) {
AlitaResult<UserDetailDTO> result = null;
result =
userService.findUserDetailDTOResult(userDetailQueryDto);
UserDetailDTO userDetailDTO = result.getData();
Assert.isTrue(result.getSuccess(), "mock失败:" + result.getMsg());
return UserMapMapper.INSTANCE.dtoToDto(userDetailDTO);
}
public static AlitaResult<UserDetailDTO> findUserDetailDTOResult(
UserDetailQueryDTO userDetailQueryDto) {
return userService.findUserDetailDTOResult(userDetailQueryDto);
}
}