Commit 7fc66be0 authored by fangxinjiang's avatar fangxinjiang

init

parent 462a5e1d
......@@ -16,7 +16,10 @@
package com.yifu.cloud.plus.v1.yifu.auth.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yifu.cloud.plus.v1.yifu.auth.filter.PasswordDecoderFilter;
import com.yifu.cloud.plus.v1.yifu.auth.handler.YifuAuthenticationFailureHandlerImpl;
import com.yifu.cloud.plus.v1.yifu.auth.handler.YifuClientLoginSuccessHandler;
import com.yifu.cloud.plus.v1.yifu.common.security.component.YifuDaoAuthenticationProvider;
import com.yifu.cloud.plus.v1.yifu.common.security.grant.CustomAppAuthenticationProvider;
import com.yifu.cloud.plus.v1.yifu.common.security.handler.FormAuthenticationFailureHandler;
......@@ -24,8 +27,11 @@ import com.yifu.cloud.plus.v1.yifu.common.security.handler.SsoLogoutSuccessHandl
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
......@@ -35,7 +41,11 @@ import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
/**
......@@ -48,6 +58,19 @@ import org.springframework.security.web.authentication.logout.LogoutSuccessHandl
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private ClientDetailsService clientDetailsService;
@Lazy
@Autowired
private AuthorizationServerTokenServices defaultAuthorizationServerTokenServices;
@Autowired
private CacheManager cacheManager;
@Autowired
private TokenStore tokenStore;
@Override
@SneakyThrows
......@@ -56,7 +79,8 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
.formLogin().loginPage("/token/login").loginProcessingUrl("/token/form")
.failureHandler(authenticationFailureHandler()).and().logout()
.logoutSuccessHandler(logoutSuccessHandler()).deleteCookies("JSESSIONID").invalidateHttpSession(true)
.and().authorizeRequests().antMatchers("/token/**", "/actuator/**", "/mobile/**", "/oauth/token").permitAll()
.and()
.authorizeRequests().antMatchers("/**/login","/token/**", "/actuator/**", "/mobile/**", "/oauth/token").permitAll()
.anyRequest().authenticated().and().csrf().disable();
}
......@@ -129,7 +153,23 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
}catch (Exception e){
log.error("WebSecurityConfigure>>>>>",e);
}
filter.setAuthenticationFailureHandler(authenticationFailureHandler());
filter.setAuthenticationSuccessHandler(yifuClientLoginSuccessHandler());
filter.setAuthenticationFailureHandler(yifuAuthenticationFailureHandler());
return filter;
}
private AuthenticationFailureHandler yifuAuthenticationFailureHandler() {
return YifuAuthenticationFailureHandlerImpl.builder()
.objectMapper(objectMapper).build();
}
private AuthenticationSuccessHandler yifuClientLoginSuccessHandler() {
return YifuClientLoginSuccessHandler.builder()
.objectMapper(objectMapper)
.clientDetailsService(clientDetailsService)
.passwordEncoder(passwordEncoder())
.cacheManager(cacheManager)
.tokenStore(tokenStore)
.defaultAuthorizationServerTokenServices(defaultAuthorizationServerTokenServices).build();
}
}
package com.yifu.cloud.plus.v1.yifu.auth.handler;
import cn.hutool.http.HttpStatus;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.exception.YifuDeniedException;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import lombok.Builder;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author fxj * @date 2022年05月30日 15:00 @description
*/
@Log4j2
@Builder
public class YifuAuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
private ObjectMapper objectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
log.info("授权失败,禁止访问 {}", request.getRequestURI());
response.setCharacterEncoding(CommonConstants.UTF8);
response.setContentType(CommonConstants.CONTENT_TYPE);
R<String> result = new R<>(null,new YifuDeniedException(e.getMessage()));
response.setStatus(HttpStatus.HTTP_FORBIDDEN);
PrintWriter printWriter = response.getWriter();
printWriter.append(objectMapper.writeValueAsString(result));
}
}
package com.yifu.cloud.plus.v1.yifu.auth.handler;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.CharsetUtil;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yifu.cloud.plus.v1.yifu.auth.constants.SecurityConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.util.Common;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import com.yifu.cloud.plus.v1.yifu.common.core.util.WebUtils;
import com.yifu.cloud.plus.v1.yifu.common.security.exception.InvalidException;
import com.yifu.cloud.plus.v1.yifu.common.security.service.YifuUser;
import com.yifu.cloud.plus.v1.yifu.common.security.util.AuthUtils;
import com.yifu.cloud.plus.v1.yifu.common.security.vo.UserAndToke;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MarkerFactory;
import org.springframework.cache.CacheManager;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
/**
* @author fxj * @date 2022年05月30日 14:55 @description
*/
@Slf4j
@Builder
public class YifuClientLoginSuccessHandler implements AuthenticationSuccessHandler {
private ObjectMapper objectMapper;
private PasswordEncoder passwordEncoder;
private ClientDetailsService clientDetailsService;
private AuthorizationServerTokenServices defaultAuthorizationServerTokenServices;
private TokenStore tokenStore;
private final CacheManager cacheManager;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String header = request.getHeader(SecurityConstants.CLIENT_HEADER_KEY_NEW);
if(Common.isEmpty(header)){
header = request.getHeader(SecurityConstants.CLIENT_HEADER_KEY);
//兼容老逻辑
if(Common.isEmpty(header)){
header = request.getHeader(HttpHeaders.AUTHORIZATION);
}
}
YifuUser user = null;
try {
user = (YifuUser)authentication.getPrincipal();
if (header == null) {
throw new InvalidClientException("请求头中client信息为空");
}
String[] tokens = AuthUtils.extractAndDecodeHeader(header);
assert tokens.length == 2;
String clientId = tokens[0];
/*boolean noThisClientId = true;
if (null != user.getOrganClientId()){
for(String id :user.getOrganClientId()){//验证单位权限
if(id.equals(clientId)){
noThisClientId = false;
break;
}
}
} else {
throw new InvalidException("所属终端为空!");
}*/
/*if (null != user.getClientRoleMap()){//验证角色
List<Integer> thisClientRoleList = user.getClientRoleMap().get(clientId);
if(null == thisClientRoleList){
if(null != ServiceNameConstants.CLIENT_NAME_MAP.get(clientId)){
clientId = ServiceNameConstants.CLIENT_NAME_MAP.get(clientId);
}
throw new InvalidException(user.getNickName()+"没有\""+ clientId +"\"的角色!请联系管理员分配角色!");
}else{
if (null != user.getAuthorities()){//剔除非登录终端的权限(权限只能重建,不能修改)
final String finalClientId = clientId;
// 构造security用户
ExtendUser thisUser = new ExtendUser(user.getId(), user.getDeptId(), user.getUsername(), " ",
user.getNickName(), user.getOrganId(),user.getDepartCode(), user.getDbKey(), user.getOrganClientId(), user.getOrganLevel(), user.isAccountNonLocked(), true, true, true, user.getAuthorities().stream().filter(auth-> { String[] authStr = String.valueOf(auth).split(":"); return authStr.length<1 || finalClientId.equals(authStr[0]);}).collect(Collectors.toSet()));
thisUser.setClientRoleMap(user.getClientRoleMap());
thisUser.setRoleDataLevelMap(user.getRoleDataLevelMap());
thisUser.setSettleIdList(user.getSettleIdList());
thisUser.setAvatar(user.getAvatar());
user = thisUser;
}
}
} else {
throw new InvalidException(user.getUsername()+"没有角色!");
}*/
/*if(noThisClientId){
if(null != ServiceNameConstants.CLIENT_NAME_MAP.get(clientId)){
clientId = ServiceNameConstants.CLIENT_NAME_MAP.get(clientId);
}
throw new InvalidException("该机构没有\""+ clientId +"\"服务权限!请联系管理员开通!");
}*/
//非管理员获取 b端结算主体权限
/*if(ServiceNameConstants.CLIENT_ID_HR_B.equals(clientId) && !SecurityUtils.isHaveAllOrg(clientId,user)){
if(Common.isEmpty(user.getSettleIdList())){
throw new InvalidException("该用户("+user.getNickName()+")没有结算主体权限!请联系管理员开通!");
}
}*/
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
//校验secret
if (!passwordEncoder.matches(tokens[1], clientDetails.getClientSecret())) {
throw new InvalidClientException("请求头中client信息验证异常");
}
TokenRequest tokenRequest = new TokenRequest(MapUtil.newHashMap(), clientId, clientDetails.getScope(), "password");
//校验scope
new DefaultOAuth2RequestValidator().validateScope(tokenRequest, clientDetails);
OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
OAuth2AccessToken oAuth2AccessToken = null;
oAuth2AccessToken = defaultAuthorizationServerTokenServices.getAccessToken(oAuth2Authentication);
if(null == oAuth2AccessToken){
oAuth2AccessToken = defaultAuthorizationServerTokenServices.createAccessToken(oAuth2Authentication);
}else{
Map<String, Object> additionalInformation = clientDetails.getAdditionalInformation();
if(null != additionalInformation && null != additionalInformation.get(SecurityConstants.MUTUALLY_EXCLUSIVE_LOGIN)){
//单点刷新老的token生成新token
tokenStore.removeAccessToken(oAuth2AccessToken);
oAuth2AccessToken = defaultAuthorizationServerTokenServices.createAccessToken(oAuth2Authentication);
}
}
log.info("获取token 成功:{}", oAuth2AccessToken.getValue());
R<UserAndToke> result = new R<>(new UserAndToke(oAuth2AccessToken,user));
backResult(response, result);
} catch (Exception e) {
if (e instanceof InvalidException){
throw (InvalidException)e ;
}else if(e instanceof InvalidClientException){
String ip = WebUtils.getIP(request);
if( null != ip && !ip.startsWith("127") && !ip.startsWith("192.168")){
log.error(MarkerFactory.getMarker("MAIL"),"{}({})登录错误(触发黑名单逻辑)ip:{},头部信息为:{}",null==user?"":user.getUsername(),null==user?"":user.getUsername(),ip, JSONObject.toJSON(WebUtils.getHeadersInfo(request)));
}else{
log.error(MarkerFactory.getMarker("MAIL"),"{}({})登录错误ip:{},头部信息为:{}",null==user?"":user.getUsername(),null==user?"":user.getUsername(),ip, JSONObject.toJSON(WebUtils.getHeadersInfo(request)));
}
throw (InvalidClientException)e;
}else{
log.error(MarkerFactory.getMarker("MAIL"),"{}({})登录错误ip:{},头部信息为:{}",null==user?"":user.getUsername(),null==user?"":user.getUsername(),WebUtils.getIP(request), JSONObject.toJSON(WebUtils.getHeadersInfo(request)));
throw new InvalidException("未知异常请联系管理员!",e);
}
}
}
private void backResult(HttpServletResponse response, R result) throws IOException {
response.setCharacterEncoding(CharsetUtil.UTF_8);
response.setContentType(CommonConstants.CONTENT_TYPE);
PrintWriter printWriter = response.getWriter();
printWriter.append(objectMapper.writeValueAsString(result));
}
}
package com.yifu.cloud.plus.v1.yifu.auth.constants;
public interface SecurityConstants {
/**
* 客户端信息key
**/
String CLIENT_HEADER_KEY_NEW = "CLIENT-HEADER";
/**
* 客户端信息key
**/
String CLIENT_HEADER_KEY = "CLIENT_HEADER";
/**
* 互斥登录的key 放在客户端信息预留信息字段additionalInformation
* 形如{"MEL":0}(有这个key则为互斥登录否则不是互斥登录)
* @Author pwang
* @Date 2021-06-03 11:28
* @param null
* @return
*@see com.yifu.cloud.v1.common.core.constant
**/
String MUTUALLY_EXCLUSIVE_LOGIN = "MEL";
}
......@@ -6,6 +6,7 @@ import cn.hutool.crypto.symmetric.AES;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
......@@ -24,6 +25,7 @@ import javax.servlet.http.HttpServletResponse;
*/
@Log4j2
@Data
@Order(-1)
@RequiredArgsConstructor
public class PasswordDecoderFilter extends UsernamePasswordAuthenticationFilter {
......
......@@ -97,4 +97,8 @@ public interface CommonConstants {
*/
String SIZE = "size";
/**
* JSON 资源
*/
}
package com.yifu.cloud.plus.v1.yifu.common.core.util;
import lombok.extern.slf4j.Slf4j;
/**
* @author: FANG
* @createDate: 2017年7月24日 下午4:50:23
* @Description 常用工具类
*/
@Slf4j
public class Common {
/**
* 字符串不为空且不是空串不是字符"null"
*
* @param str
* @return
* @author: fxj
*/
public static boolean isEmpty(String str) {
return null == str || "".equals(str.trim()) || "null".equals(str.trim()) || "undefinded".equals(str.trim());
}
/**
* 对象null
*
* @param obj
* @return
* @author: fxj
*/
public static boolean isEmpty(Object obj) {
return null == obj;
}
}
......@@ -48,6 +48,17 @@ public class R<T> implements Serializable {
@Setter
private T data;
public R(T data) {
super();
this.data = data;
}
//这个异常类不要放开,new R(null)容易出现问题
public R(T data,Throwable e) {
super();
this.msg = e.getMessage();
this.code = CommonConstants.FAIL;
}
public static <T> R<T> ok() {
return restResult(null, CommonConstants.SUCCESS, null);
}
......
......@@ -20,12 +20,14 @@ import cn.hutool.core.codec.Base64;
import cn.hutool.json.JSONUtil;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.exception.CheckedException;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
......@@ -38,6 +40,9 @@ import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
......@@ -195,5 +200,60 @@ public class WebUtils extends org.springframework.web.util.WebUtils {
}
return new String[] { token.substring(0, delim), token.substring(delim + 1) };
}
/**
* 获取ip
*
* @return {String}
*/
public static String getIP() {
return getIP(WebUtils.getRequest().get());
}
/**
* 获取ip
*
* @param request HttpServletRequest
* @return {String}
*/
public static String getIP(HttpServletRequest request) {
Assert.notNull(request, "HttpServletRequest is null");
String ip = request.getHeader("X-Requested-For");
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return StringUtils.isBlank(ip) ? null : ip.split(",")[0];
}
/**
* 获取所有头部信息
* @Author pwang
* @Date 2021-07-01 15:50
* @param request
* @return
**/
public static Map<String, String> getHeadersInfo(HttpServletRequest request) {
Map<String, String> map = new HashMap<String, String>();
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
return map;
}
}
package com.yifu.cloud.plus.v1.yifu.common.security.vo;
import com.yifu.cloud.plus.v1.yifu.common.security.service.YifuUser;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
/**
* Description:
* date: 2019-06-28 17:23
*
* @author pwang
* @since JDK 1.8
*/
@Data
@AllArgsConstructor
public class UserAndToke {
private OAuth2AccessToken oauth2AccessToken;
private YifuUser user;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment