Introducing new features. support sas 认证中心改造

This commit is contained in:
lbw 2022-05-27 15:28:17 +08:00
parent 64773a1f1a
commit e4d9213c1e
8 changed files with 310 additions and 572 deletions

View File

@ -16,83 +16,92 @@
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>3.4.10</version>
</parent>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>3.4.10</version>
</parent>
<artifactId>pig-auth</artifactId>
<packaging>jar</packaging>
<artifactId>pig-auth</artifactId>
<packaging>jar</packaging>
<description>pig 认证授权中心,基于 spring security oAuth2</description>
<description>pig 认证授权中心,基于 spring security oAuth2</description>
<dependencies>
<!--注册中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--断路器依赖-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-feign</artifactId>
</dependency>
<!--upms api、model 模块-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-upms-api</artifactId>
</dependency>
<!--security-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-security</artifactId>
</dependency>
<!--JDBC相关-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--freemarker-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--undertow容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- log -->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-log</artifactId>
</dependency>
</dependencies>
<dependencies>
<!--注册中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--断路器依赖-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-feign</artifactId>
</dependency>
<!--upms api、model 模块-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-upms-api</artifactId>
</dependency>
<!--security-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.3.0</version>
</dependency>
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--JDBC相关-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--freemarker-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--undertow容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- log -->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-log</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -16,50 +16,60 @@
package com.pig4cloud.pig.auth.config;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.security.component.PigWebResponseExceptionTranslator;
import com.pig4cloud.pig.common.security.grant.ResourceOwnerCustomAppTokenGranter;
import com.pig4cloud.pig.common.security.service.PigClientDetailsService;
import com.pig4cloud.pig.common.security.service.PigCustomTokenServices;
import com.pig4cloud.pig.common.security.service.PigUser;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import com.nimbusds.jose.jwk.source.JWKSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2TokenFormat;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.*;
import org.springframework.security.web.SecurityFilterChain;
/**
* @author lengleng
* @date 2022/5/27 认证服务器配置
*/
@Configuration
@RequiredArgsConstructor
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfiguration {
private final DataSource dataSource;
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
private final AuthenticationManager authenticationManager;
return http.formLogin(Customizer.withDefaults()).build();
}
private final TokenStore redisTokenStore;
// @formatter:off
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient client = RegisteredClient.withId("pig")
.clientId("pig")
.clientSecret("{noop}pig")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantTypes(authorizationGrantTypes -> {
authorizationGrantTypes.add(AuthorizationGrantType.AUTHORIZATION_CODE);
authorizationGrantTypes.add(AuthorizationGrantType.REFRESH_TOKEN);
})
.tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED).build())
.redirectUri("https://pig4cloud.com")
.build();
return new InMemoryRegisteredClientRepository(client);
}
// @formatter:on
@Bean
@ -71,31 +81,14 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu
return new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}
/**
* 客户端信息加载处理
* @return ClientDetailsService
*/
@Bean
public ClientDetailsService pigClientDetailsService() {
PigClientDetailsService clientDetailsService = new PigClientDetailsService(dataSource);
clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
return clientDetailsService;
public ProviderSettings providerSettings() {
return ProviderSettings.builder().build();
}
/**
* token 核心处理
* @return tokenServices
*/
@Bean
public PigCustomTokenServices tokenServices() {
PigCustomTokenServices tokenServices = new PigCustomTokenServices();
tokenServices.setTokenStore(redisTokenStore);
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(false);
tokenServices.setClientDetailsService(pigClientDetailsService());
tokenServices.setTokenEnhancer(tokenEnhancer());
return tokenServices;
public OAuth2AuthorizationService authorizationService() {
return new InMemoryOAuth2AuthorizationService();
}
}

View File

@ -16,102 +16,44 @@
package com.pig4cloud.pig.auth.config;
import com.pig4cloud.pig.common.security.component.PigDaoAuthenticationProvider;
import com.pig4cloud.pig.common.security.grant.CustomAppAuthenticationProvider;
import com.pig4cloud.pig.common.security.handler.FormAuthenticationFailureHandler;
import com.pig4cloud.pig.common.security.handler.SsoLogoutSuccessHandler;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.Customizer;
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.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
/**
* @author lengleng
* @date 2022/1/12 认证相关配置
*/
@Primary
@Order(90)
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
@SneakyThrows
protected void configure(HttpSecurity http) {
http.formLogin().loginPage("/token/login").loginProcessingUrl("/token/form")
.failureHandler(authenticationFailureHandler()).and().logout()
.logoutSuccessHandler(logoutSuccessHandler()).deleteCookies("JSESSIONID").invalidateHttpSession(true)
.and().authorizeRequests().antMatchers("/token/**", "/actuator/**", "/mobile/**").permitAll()
.anyRequest().authenticated().and().csrf().disable();
}
/**
* 自定义 provider 列表注入
* @param auth AuthenticationManagerBuilder
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) {
PigDaoAuthenticationProvider daoAuthenticationProvider = new PigDaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
// 处理默认的密码模式认证
auth.authenticationProvider(daoAuthenticationProvider);
// 自定义的认证模式
auth.authenticationProvider(new CustomAppAuthenticationProvider());
}
@EnableWebSecurity
public class WebSecurityConfiguration {
// @formatter:off
@Bean
@Override
@SneakyThrows
public AuthenticationManager authenticationManagerBean() {
return super.authenticationManagerBean();
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests -> authorizeRequests
.antMatchers("/password/*").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
/**
* 认证中心静态资源处理
* @param web WebSecurity
*/
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/css/**", "/error");
}
/**
* sso 表单登录失败处理
* @return FormAuthenticationFailureHandler
*/
// @formatter:off
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new FormAuthenticationFailureHandler();
UserDetailsService users() {
UserDetails user = User.builder()
.username("admin")
.password("{noop}123456")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
/**
* SSO 退出逻辑处理
* @return LogoutSuccessHandler
*/
@Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new SsoLogoutSuccessHandler();
}
/**
* 密码处理器
* @return 动态密码处理器 {类型}密文
*/
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}

View File

@ -0,0 +1,153 @@
package com.pig4cloud.pig.auth.endpoint;
import cn.hutool.extra.spring.SpringUtil;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.security.service.PigUserDetailsService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.core.Ordered;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.*;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* 登录端点
*
* @author lengleng
* @date 2022/5/27
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/password")
public class LoginEndpoint {
private final OAuth2AuthorizationService tokenService;
private final RegisteredClientRepository registeredClientRepository;
private final OAuth2TokenGenerator tokenGenerator;
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
private final OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
@SneakyThrows
@RequestMapping("/login")
public void login(HttpServletResponse response, HttpServletRequest request) {
RegisteredClient client = registeredClientRepository.findByClientId("pig");
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("admin",
"123");
// @formatter:off
DefaultOAuth2TokenContext.Builder builder = DefaultOAuth2TokenContext.builder()
.registeredClient(client)
.principal(authenticationToken)
.tokenType(OAuth2TokenType.ACCESS_TOKEN)
.authorizationGrantType(AuthorizationGrantType.PASSWORD);
// @formatter:on
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(builder.build());
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
generatedAccessToken.getExpiresAt(), builder.build().getAuthorizedScopes());
// @formatter:off
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(client)
.principalName("pig")
.principalName(authenticationToken.getName())
.authorizationGrantType(AuthorizationGrantType.PASSWORD);
// @formatter:on
if (generatedAccessToken instanceof ClaimAccessor) {
authorizationBuilder.token(accessToken,
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
((ClaimAccessor) generatedAccessToken).getClaims()));
}
else {
authorizationBuilder.accessToken(accessToken);
}
OAuth2Token generatedRefreshToken = this.refreshTokenGenerator
.generate(builder.tokenType(OAuth2TokenType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).build());
authorizationBuilder.refreshToken((OAuth2RefreshToken) generatedRefreshToken);
OAuth2Authorization authorization = authorizationBuilder.build();
tokenService.save(authorization);
Map<String, Object> additionalParameters = new HashMap<>();
additionalParameters.put("license", "pig");
Map<String, PigUserDetailsService> userDetailsServiceMap = SpringUtil
.getBeansOfType(PigUserDetailsService.class);
Optional<PigUserDetailsService> optional = userDetailsServiceMap.values().stream()
.filter(service -> service.support(client.getClientId(), "password"))
.max(Comparator.comparingInt(Ordered::getOrder));
UserDetails admin = optional.get().loadUserByUsername("admin");
additionalParameters.put(SecurityConstants.DETAILS_USER, admin);
OAuth2AccessTokenAuthenticationToken oAuth2AccessTokenAuthenticationToken = new OAuth2AccessTokenAuthenticationToken(
client, authenticationToken, accessToken, (OAuth2RefreshToken) generatedRefreshToken,
additionalParameters);
sendAccessTokenResponse(request, response, oAuth2AccessTokenAuthenticationToken);
}
@GetMapping("/info")
public OAuth2Authorization info(String token) {
return tokenService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
}
private void sendAccessTokenResponse(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication;
OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
.tokenType(accessToken.getTokenType()).scopes(accessToken.getScopes());
if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {
builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()));
}
if (refreshToken != null) {
builder.refreshToken(refreshToken.getTokenValue());
}
if (!CollectionUtils.isEmpty(additionalParameters)) {
builder.additionalParameters(additionalParameters);
}
OAuth2AccessTokenResponse accessTokenResponse = builder.build();
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
}
}

View File

@ -1,168 +0,0 @@
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* 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.pig4cloud.pig.auth.endpoint;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.common.core.constant.CacheConstants;
import com.pig4cloud.pig.common.core.constant.CommonConstants;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.common.security.annotation.Inner;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.event.LogoutSuccessEvent;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author lengleng
* @date 2019/2/1 删除token端点
*/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/token")
public class PigTokenEndpoint {
private final ClientDetailsService clientDetailsService;
private final TokenStore tokenStore;
private final RedisTemplate redisTemplate;
private final CacheManager cacheManager;
/**
* 认证页面
* @param modelAndView
* @param error 表单登录失败处理回调的错误信息
* @return ModelAndView
*/
@GetMapping("/login")
public ModelAndView require(ModelAndView modelAndView, @RequestParam(required = false) String error) {
modelAndView.setViewName("ftl/login");
modelAndView.addObject("error", error);
return modelAndView;
}
/**
* 确认授权页面
* @param request
* @param session
* @param modelAndView
* @return
*/
@GetMapping("/confirm_access")
public ModelAndView confirm(HttpServletRequest request, HttpSession session, ModelAndView modelAndView) {
Map<String, Object> scopeList = (Map<String, Object>) request.getAttribute("scopes");
modelAndView.addObject("scopeList", scopeList.keySet());
Object auth = session.getAttribute("authorizationRequest");
if (auth != null) {
AuthorizationRequest authorizationRequest = (AuthorizationRequest) auth;
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId());
modelAndView.addObject("app", clientDetails.getAdditionalInformation());
modelAndView.addObject("user", SecurityUtils.getUser());
}
modelAndView.setViewName("ftl/confirm");
return modelAndView;
}
/**
* 退出并删除token
* @param authHeader Authorization
*/
@DeleteMapping("/logout")
public R<Boolean> logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) {
if (StrUtil.isBlank(authHeader)) {
return R.ok();
}
String tokenValue = authHeader.replace(OAuth2AccessToken.BEARER_TYPE, StrUtil.EMPTY).trim();
return removeToken(tokenValue);
}
/**
* 令牌管理调用
* @param token token
*/
@Inner
@DeleteMapping("/{token}")
public R<Boolean> removeToken(@PathVariable("token") String token) {
OAuth2AccessToken accessToken = tokenStore.readAccessToken(token);
if (accessToken == null || StrUtil.isBlank(accessToken.getValue())) {
return R.ok();
}
OAuth2Authentication auth2Authentication = tokenStore.readAuthentication(accessToken);
// 清空用户信息
cacheManager.getCache(CacheConstants.USER_DETAILS).evict(auth2Authentication.getName());
// 清空access token
tokenStore.removeAccessToken(accessToken);
// 清空 refresh token
OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
tokenStore.removeRefreshToken(refreshToken);
// 处理自定义退出事件保存相关日志
SpringContextHolder.publishEvent(new LogoutSuccessEvent(auth2Authentication));
return R.ok();
}
/**
* 查询token
* @param params 分页参数
* @return
*/
@Inner
@PostMapping("/page")
public R<Page> tokenList(@RequestBody Map<String, Object> params) {
// 根据分页参数获取对应数据
String key = String.format("%sauth_to_access:*", CacheConstants.PROJECT_OAUTH_ACCESS);
int current = MapUtil.getInt(params, CommonConstants.CURRENT);
int size = MapUtil.getInt(params, CommonConstants.SIZE);
Set<String> keys = redisTemplate.keys(key);
List<String> pages = keys.stream().skip((current - 1) * size).limit(size).collect(Collectors.toList());
Page result = new Page(current, size);
result.setRecords(redisTemplate.opsForValue().multiGet(pages));
result.setTotal(keys.size());
return R.ok(result);
}
}

View File

@ -1,62 +0,0 @@
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* 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.pig4cloud.pig.auth.handler;
import com.pig4cloud.pig.admin.api.entity.SysLog;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.common.log.event.SysLogEvent;
import com.pig4cloud.pig.common.log.util.LogTypeEnum;
import com.pig4cloud.pig.common.log.util.SysLogUtils;
import com.pig4cloud.pig.common.security.handler.AbstractAuthenticationFailureEventHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
/**
* @author lengleng
* @date 2019/2/1
*/
@Slf4j
@Component
public class PigAuthenticationFailureEventHandler extends AbstractAuthenticationFailureEventHandler {
/**
* 处理登录失败方法
* <p>
* @param authenticationException 登录的authentication 对象
* @param authentication 登录的authenticationException 对象
*/
@Override
public void handle(AuthenticationException authenticationException, Authentication authentication) {
log.info("用户:{} 登录失败,异常:{}", authentication.getPrincipal(), authenticationException.getLocalizedMessage());
SecurityContextHolder.getContext().setAuthentication(authentication);
SysLog logVo = SysLogUtils.getSysLog();
logVo.setTitle("登录失败");
logVo.setType(LogTypeEnum.ERROR.getType());
logVo.setException(authenticationException.getMessage());
// 发送异步日志事件
Long startTime = System.currentTimeMillis();
Long endTime = System.currentTimeMillis();
logVo.setTime(endTime - startTime);
logVo.setCreateBy(authentication.getName());
logVo.setUpdateBy(authentication.getName());
SpringContextHolder.publishEvent(new SysLogEvent(logVo));
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* 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.pig4cloud.pig.auth.handler;
import com.pig4cloud.pig.admin.api.entity.SysLog;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.common.log.event.SysLogEvent;
import com.pig4cloud.pig.common.log.util.SysLogUtils;
import com.pig4cloud.pig.common.security.handler.AbstractAuthenticationSuccessEventHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
/**
* @author lengleng
* @date 2019/2/1
*/
@Slf4j
@Component
public class PigAuthenticationSuccessEventHandler extends AbstractAuthenticationSuccessEventHandler {
/**
* 处理登录成功方法
* <p>
* 获取到登录的authentication 对象
* @param authentication 登录对象
*/
@Override
public void handle(Authentication authentication) {
log.info("用户:{} 登录成功", authentication.getPrincipal());
SecurityContextHolder.getContext().setAuthentication(authentication);
SysLog logVo = SysLogUtils.getSysLog();
logVo.setTitle("登录成功");
// 发送异步日志事件
Long startTime = System.currentTimeMillis();
Long endTime = System.currentTimeMillis();
logVo.setTime(endTime - startTime);
logVo.setCreateBy(authentication.getName());
logVo.setUpdateBy(authentication.getName());
SpringContextHolder.publishEvent(new SysLogEvent(logVo));
}
}

View File

@ -1,71 +0,0 @@
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* 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.pig4cloud.pig.auth.handler;
import com.pig4cloud.pig.admin.api.entity.SysLog;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.common.core.util.WebUtils;
import com.pig4cloud.pig.common.log.event.SysLogEvent;
import com.pig4cloud.pig.common.log.util.SysLogUtils;
import com.pig4cloud.pig.common.security.handler.AbstractLogoutSuccessEventHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Component;
/**
* @author zhangran
* @date 2021/6/23
*/
@Slf4j
@Component
public class PigLogoutSuccessEventHandler extends AbstractLogoutSuccessEventHandler {
/**
* 处理退出成功方法
* <p>
* 获取到登录的authentication 对象
* @param authentication 登录对象
*/
@Override
public void handle(Authentication authentication) {
log.info("用户:{} 退出成功", authentication.getPrincipal());
SecurityContextHolder.getContext().setAuthentication(authentication);
SysLog logVo = SysLogUtils.getSysLog();
logVo.setTitle("退出成功");
// 发送异步日志事件
Long startTime = System.currentTimeMillis();
Long endTime = System.currentTimeMillis();
logVo.setTime(endTime - startTime);
// 设置对应的token
WebUtils.getRequest().ifPresent(request -> logVo.setParams(request.getHeader(HttpHeaders.AUTHORIZATION)));
// 这边设置ServiceId
if (authentication instanceof OAuth2Authentication) {
OAuth2Authentication auth2Authentication = (OAuth2Authentication) authentication;
logVo.setServiceId(auth2Authentication.getOAuth2Request().getClientId());
}
logVo.setCreateBy(authentication.getName());
logVo.setUpdateBy(authentication.getName());
SpringContextHolder.publishEvent(new SysLogEvent(logVo));
}
}