Commit b2f117ab authored by huyuchen's avatar huyuchen

cas修改

parent 6e103eca
......@@ -4,12 +4,12 @@ public class CASproperties {
/**
* 当前应用程序的baseUrl(注意最后面的斜线)
*/
public static String CLIENT_SERVER_NAME = "http://127.0.0.1:8888/";
public static String CLIENT_SERVER_NAME = "https://qas-mvp-1.worfu.com/";
/**
* 当前应用程序的登陆界面
*/
public static String CLIENT_LOGIN_PAGE = "http://127.0.0.1:8888/login";
public static String CLIENT_LOGIN_PAGE = "https://qas-mvp-1.worfu.com/login";
/**
* CAS服务器地址
......@@ -25,4 +25,6 @@ public class CASproperties {
* CAS登出服务器地址
*/
public static String CAS_SERVER_LOGOUT_PATH = "http://192.168.1.62:8443/cas/logout";
public static String SPRING_SECURITY_CAS_SERVER = "http://192.168.1.62:8443/cas/v1/tickets";
}
......@@ -20,10 +20,12 @@ 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.CasAuthenticationProvider;
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;
import com.yifu.cloud.plus.v1.yifu.common.security.handler.SsoLogoutSuccessHandler;
import com.yifu.cloud.plus.v1.yifu.common.security.service.YifuUserDetailsServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
......@@ -72,6 +74,9 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Autowired
private YifuUserDetailsServiceImpl usernameUserDetailService;
@Override
@SneakyThrows
protected void configure(HttpSecurity http) {
......@@ -97,6 +102,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
auth.authenticationProvider(daoAuthenticationProvider);
// 自定义的认证模式
auth.authenticationProvider(new CustomAppAuthenticationProvider());
auth.authenticationProvider(casAuthenticationProvider());
}
@Bean
......@@ -142,6 +148,16 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider provider = new CasAuthenticationProvider();
// 设置userDetailsService
provider.setUserDetailsService(usernameUserDetailService);
// 禁止隐藏用户未找到异常
provider.setHideUserNotFoundExceptions(false);
return provider;
}
/**
* @author fxj
* @date 2022/5/27 17:22
......
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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.yifu.cloud.plus.v1.yifu.auth.controller;
import com.yifu.cloud.plus.v1.yifu.auth.config.CASproperties;
import com.yifu.cloud.plus.v1.yifu.auth.util.LoginUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.logging.Logger;
/**
* @author huyc
* @since 1.6.0
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/auth")
public class AuthController {
private static final Logger LOG = Logger.getLogger(AuthController.class.getName());
@PostMapping("/login")
public String login(HttpServletRequest request, String username, String password) {
return null;
}
@PostMapping(value = "/logout")
public void logout(HttpServletRequest request) {
request.getSession().invalidate();
}
@PostMapping(value = "/check")
public String check(HttpServletRequest request, String username, String password) {
String st = LoginUtils.getTicket(CASproperties.CAS_SERVER_LOGOUT_PATH,username,password,CASproperties.CLIENT_LOGIN_PAGE);
LOG.info(st);
return st;
}
}
......@@ -3,15 +3,23 @@ package com.yifu.cloud.plus.v1.yifu.auth.filter;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import com.yifu.cloud.plus.v1.yifu.auth.config.CASproperties;
import com.yifu.cloud.plus.v1.yifu.common.core.util.Common;
import com.yifu.cloud.plus.v1.yifu.common.security.token.CasAuthenticationToken;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.http.*;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
......@@ -23,7 +31,7 @@ import javax.servlet.http.HttpServletResponse;
* @date 2022年05月27日 11:14
* @description
*/
@Log4j2
@Slf4j
@Data
@Order(-1)
@RequiredArgsConstructor
......@@ -33,7 +41,6 @@ public class PasswordDecoderFilter extends UsernamePasswordAuthenticationFilter
/**
* 解密登录前端密码 秘钥
*/
//private String encodeKey = "thanks,pig4cloud";
private String encodeKey = "thanks,yifucloud";
private boolean postOnly = true;
......@@ -43,17 +50,41 @@ public class PasswordDecoderFilter extends UsernamePasswordAuthenticationFilter
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
AbstractAuthenticationToken authRequest;
String result = request.getParameter("cas");
String username = this.obtainUsername(request);
username = username != null ? username : "";
username = username.trim();
String password = this.obtainPassword(request);
password = password != null ? decryptAES(password) : "";
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
this.setDetails(request, authRequest);
if (Common.isNotNull(result)) {
try {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String,Object> wholeForm = new LinkedMultiValueMap<>();
wholeForm.add("service", CASproperties.CLIENT_LOGIN_PAGE);
HttpEntity<MultiValueMap<String,Object>> entity = new org.springframework.http.HttpEntity<>(wholeForm,headers);
ResponseEntity<String> obj = restTemplate.exchange(CASproperties.SPRING_SECURITY_CAS_SERVER + "/" + result, HttpMethod.POST, entity, String.class);
if (obj.getStatusCode().value() == 200) {
String st = obj.getBody();
log.info("Successful service ticket request: " + st);
}
} catch (Exception e) {
log.error(e.getMessage());
throw new AuthenticationServiceException("验证失败!");
}
authRequest = new CasAuthenticationToken(username, null);
} else {
String password = this.obtainPassword(request);
password = password != null ? decryptAES(password) : "";
authRequest = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, authRequest);
}
return this.getAuthenticationManager().authenticate(authRequest);
}
}
/**
* 原文解密
* @return
......@@ -66,4 +97,9 @@ public class PasswordDecoderFilter extends UsernamePasswordAuthenticationFilter
// 解密
return aes.decryptStr(password);
}
private void setDetails(HttpServletRequest request,
AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}
package com.yifu.cloud.plus.v1.yifu.auth.util;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.validation.Assertion;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.util.logging.Logger;
public class LoginUtils {
......@@ -48,14 +52,12 @@ public class LoginUtils {
HttpEntity<MultiValueMap<String,Object>> entity = new HttpEntity<>(wholeForm,headers);
ResponseEntity<String> obj = restTemplate.exchange(server + "/" + ticketGrantingTicket, HttpMethod.POST, entity, String.class);
switch (obj.getStatusCode().value()) {
case 200:
LOG.info("Successful service ticket request: " + obj.getBody());
return obj.getBody();
default:
LOG.warning("Invalid response code (" + obj.getStatusCode().value() + ") from CAS server!");
LOG.info("Response (1k): " + obj.getBody());
break;
if (obj.getStatusCode().value() == 200) {
LOG.info("Successful service ticket request: " + obj.getBody());
return obj.getBody();
} else {
LOG.warning("Invalid response code (" + obj.getStatusCode().value() + ") from CAS server!");
LOG.info("Response (1k): " + obj.getBody());
}
} catch (Exception e) {
LOG.warning(e.getMessage());
......@@ -83,18 +85,36 @@ public class LoginUtils {
wholeForm.add("password",password);
HttpEntity<MultiValueMap<String,Object>> entity = new HttpEntity<>(wholeForm,headers);
ResponseEntity<String> obj = restTemplate.exchange(server, HttpMethod.POST, entity, String.class);
switch (obj.getStatusCode().value()) {
case 201:
LOG.info("Successful ticket granting ticket request: " + obj.getBody());
return obj.getBody();
default:
LOG.warning("Invalid response code (" + obj.getStatusCode().value() + ") from CAS server!");
LOG.info("Response (1k): " + obj.getBody());
break;
if (obj.getStatusCode().value() == 201) {
LOG.info("Successful ticket granting ticket request: " + obj.getBody());
return obj.getBody();
} else {
LOG.warning("Invalid response code (" + obj.getStatusCode().value() + ") from CAS server!");
LOG.info("Response (1k): " + obj.getBody());
}
} catch (Exception e) {
LOG.warning(e.getMessage());
}
return null;
}
//从cas中获取用户名
public static String getAccountNameFromCas(HttpServletRequest request) {
Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
if (assertion != null) {
AttributePrincipal principal = assertion.getPrincipal();
return principal.getName();
} else {
return null;
}
}
public static void main(final String[] args) {
final String server = "http://192.168.1.62:8443/cas/v1/tickets";
final String username = "zhangfei2";
final String password = "123456";
final String service = "http://127.0.0.1:8888/login";
LOG.info(getTicket(server, username, password, service));
}
}
package com.yifu.cloud.plus.v1.yifu.common.security.component;
import com.yifu.cloud.plus.v1.yifu.common.security.token.CasAuthenticationToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* cas验证登陆
*/
@Slf4j
public class CasAuthenticationProvider extends MyAbstractUserDetailsAuthenticationProvider {
private UserDetailsService userDetailsService;
@Override
protected void additionalAuthenticationChecks(UserDetails var1, Authentication authentication) throws AuthenticationException {
}
@Override
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
CasAuthenticationToken result = new CasAuthenticationToken(principal, authentication.getCredentials(), user.getAuthorities());
result.setDetails(authentication.getDetails());
return result;
}
@Override
protected UserDetails retrieveUser(String casUserName, Authentication authentication) throws AuthenticationException {
UserDetails loadedUser;
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(casUserName);
} catch (UsernameNotFoundException var6) {
throw var6;
} catch (Exception var7) {
log.info("CasAuthenticationProvider>>>>>>",var7);
throw new InternalAuthenticationServiceException(var7.getMessage(), var7);
}
if(loadedUser == null) {
throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
} else {
return loadedUser;
}
}
@Override
public boolean supports(Class<?> authentication) {
return CasAuthenticationToken.class.isAssignableFrom(authentication);
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}
package com.yifu.cloud.plus.v1.yifu.common.security.component;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.cache.NullUserCache;
import org.springframework.util.Assert;
/**
* Created by fp295 on 2023/3/14.
* 自定义 AuthenticationProvider, 以使用自定义的 MyAuthenticationToken
*/
public abstract class MyAbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
protected final Log logger = LogFactory.getLog(this.getClass());
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private UserCache userCache = new NullUserCache();
private boolean forcePrincipalAsString = false;
protected boolean hideUserNotFoundExceptions = true;
private UserDetailsChecker preAuthenticationChecks = new DefaultPreAuthenticationChecks();
private UserDetailsChecker postAuthenticationChecks = new DefaultPostAuthenticationChecks();
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
protected abstract void additionalAuthenticationChecks(UserDetails var1, Authentication var2) throws AuthenticationException;
public final void afterPropertiesSet() throws Exception {
Assert.notNull(this.userCache, "A user cache must be set");
Assert.notNull(this.messages, "A message source must be set");
this.doAfterPropertiesSet();
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getPrincipal() == null?"NONE_PROVIDED":authentication.getName();
boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);
if(user == null) {
cacheWasUsed = false;
try {
user = this.retrieveUser(username, authentication);
} catch (UsernameNotFoundException var6) {
this.logger.debug("User \'" + username + "\' not found");
if(this.hideUserNotFoundExceptions) {
throw new BadCredentialsException(this.messages.getMessage("MyAbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
throw var6;
}
Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
}
try {
this.preAuthenticationChecks.check(user);
this.additionalAuthenticationChecks(user, authentication);
} catch (AuthenticationException var7) {
if(!cacheWasUsed) {
throw var7;
}
cacheWasUsed = false;
user = this.retrieveUser(username, authentication);
this.preAuthenticationChecks.check(user);
this.additionalAuthenticationChecks(user, authentication);
}
this.postAuthenticationChecks.check(user);
if(!cacheWasUsed) {
this.userCache.putUserInCache(user);
}
Object principalToReturn = user;
if(this.forcePrincipalAsString) {
principalToReturn = user.getUsername();
}
return this.createSuccessAuthentication(principalToReturn, authentication, user);
}
protected abstract Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user);
protected void doAfterPropertiesSet() throws Exception {
}
public UserCache getUserCache() {
return this.userCache;
}
public boolean isForcePrincipalAsString() {
return this.forcePrincipalAsString;
}
public boolean isHideUserNotFoundExceptions() {
return this.hideUserNotFoundExceptions;
}
protected abstract UserDetails retrieveUser(String var1, Authentication var2) throws AuthenticationException;
public void setForcePrincipalAsString(boolean forcePrincipalAsString) {
this.forcePrincipalAsString = forcePrincipalAsString;
}
public void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {
this.hideUserNotFoundExceptions = hideUserNotFoundExceptions;
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
public void setUserCache(UserCache userCache) {
this.userCache = userCache;
}
protected UserDetailsChecker getPreAuthenticationChecks() {
return this.preAuthenticationChecks;
}
public void setPreAuthenticationChecks(UserDetailsChecker preAuthenticationChecks) {
this.preAuthenticationChecks = preAuthenticationChecks;
}
protected UserDetailsChecker getPostAuthenticationChecks() {
return this.postAuthenticationChecks;
}
public void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks) {
this.postAuthenticationChecks = postAuthenticationChecks;
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
private class DefaultPostAuthenticationChecks implements UserDetailsChecker {
private DefaultPostAuthenticationChecks() {
}
public void check(UserDetails user) {
if(!user.isCredentialsNonExpired()) {
MyAbstractUserDetailsAuthenticationProvider.this.logger.debug("User account credentials have expired");
throw new CredentialsExpiredException(MyAbstractUserDetailsAuthenticationProvider.this.messages.getMessage("MyAbstractUserDetailsAuthenticationProvider.credentialsExpired", "User credentials have expired"));
}
}
}
private class DefaultPreAuthenticationChecks implements UserDetailsChecker {
private DefaultPreAuthenticationChecks() {
}
public void check(UserDetails user) {
if(!user.isAccountNonLocked()) {
MyAbstractUserDetailsAuthenticationProvider.this.logger.debug("User account is locked");
throw new LockedException(MyAbstractUserDetailsAuthenticationProvider.this.messages.getMessage("MyAbstractUserDetailsAuthenticationProvider.locked", "用户帐号已被锁定"));
} else if(!user.isEnabled()) {
MyAbstractUserDetailsAuthenticationProvider.this.logger.debug("User account is disabled");
throw new DisabledException(MyAbstractUserDetailsAuthenticationProvider.this.messages.getMessage("MyAbstractUserDetailsAuthenticationProvider.disabled", "用户未激活"));
} else if(!user.isAccountNonExpired()) {
MyAbstractUserDetailsAuthenticationProvider.this.logger.debug("User account is expired");
throw new AccountExpiredException(MyAbstractUserDetailsAuthenticationProvider.this.messages.getMessage("MyAbstractUserDetailsAuthenticationProvider.expired", "用户帐号已过期"));
}
}
}
}
package com.yifu.cloud.plus.v1.yifu.common.security.token;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
/**
* cas token
*/
public class CasAuthenticationToken extends MyAuthenticationToken {
public CasAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public CasAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}
package com.yifu.cloud.plus.v1.yifu.common.security.token;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
/**
* 自定义AbstractAuthenticationToken,
*/
public class MyAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 110L;
protected final Object principal;
protected Object credentials;
/**
* This constructor can be safely used by any code that wishes to create a
* <code>UsernamePasswordAuthenticationToken</code>, as the {@link
* #isAuthenticated()} will return <code>false</code>.
*
*/
public MyAuthenticationToken(Object principal, Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
/**
* This constructor should only be used by <code>AuthenticationManager</code> or <code>AuthenticationProvider</code>
* implementations that are satisfied with producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
* token token.
*
* @param principal
* @param credentials
* @param authorities
*/
public MyAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
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");
} else {
super.setAuthenticated(false);
}
}
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}
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