需要将请求通过zuul网关转发到后端,同时,zuul作为AuthorizeServer,集成了Eureka,通过Eureka做服务发现.
首先生成一个Eureka server ,非常简单
more application.properties
server.port=7999
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
spring.application.name=eureka-server
eureka.instance.hostname=localhost
eureka.client.serviceUrl.defaultZone=http://localhost:7999/eureka/
eureka.client.enabled=true
eureka.client.allow-redirects=true
eureka.server.eviction-interval-timer-in-ms=10000
然后配置一个ResourceServer
more application.properties
server.port=8081
spring.application.name=biz
security.oauth2.client.client-id=biz
security.oauth2.client.client-secret=bizsecret
security.oauth2.resource.id=biz
security.oauth2.resource.token-info-uri=http://localhost:8080/oauth/check_token
security.oauth2.authorization.check-token-access=http://localhost:8080/oauth/check_token
security.oauth2.resource.prefer-token-info=true
logging.level.org.springframework.security=DEBUG
spring_scurity_token_access_url=http://localhost:8080/oauth/token
#security.basic.enabled=true
#spring.redis.database=0
#spring.redis.cluster.nodes=10.20.100.228:6376,10.20.100.228:6377,10.20.100.228:6378
#spring.redis.cluster.max-redirects=3
#spring.redis.jedis.pool.max-active=50
#spring.redis.jedis.pool.max-idle=10
#spring.redis.jedis.pool.min-idle=1
#spring.redis.jedis.pool.max-wait=3000
#spring.redis.port=6377
spring.redis.port=6379
spring.redis.database=0
spring.redis.host=10.20.100.235
#spring.redis.pool.max-active=50
#spring.redis.pool.max-idle=10
#spring.redis.pool.max-wait=3000
#spring.redis.pool.min-idle=3
grails.mongodb.url=mongodb://localhost:27017/wsbiz
grails.mongodb.engine=codec
grails.mongodb.options.autoConnectRetry=true
grails.mongodb.options.connectTimeout=30000
spring.data.mongodb.uri=mongodb://localhost:27017/wensheng
### actuator##
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true
management.endpoint.health.show-details=always
###eureka
###eureka
eureka.client.allow-redirects=true
eureka.client.enabled=true
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://localhost:7999/eureka/
在biz的Application上加上
@SpringBootApplication
@EnableResourceServer
@EnableEurekaClient
class BizApplication{
...
}
正常配置tokenStore,用来解析用户的内容
package com.ws.biz
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.http.HttpMethod
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer
import org.springframework.security.oauth2.provider.token.DefaultTokenServices
import org.springframework.security.oauth2.provider.token.TokenStore
import org.springframework.web.client.RestTemplate
import java.nio.charset.StandardCharsets
import java.util.concurrent.TimeUnit
@Configuration
class ResourceConf extends ResourceServerConfigurerAdapter{
@Bean
RestTemplate restTemplate(){
def restTemplate = new RestTemplate()
restTemplate.getMessageConverters().set(1,new org.springframework.http.converter.StringHttpMessageConverter(StandardCharsets.UTF_8))
return restTemplate
}
@Autowired
RedisTemplate redisTemplate
@Bean
public TokenStore tokenStore() {
return new JedisTokenStore(redisTemplate: redisTemplate)
}
@Bean
@Primary //这里必须定义primary 让resource在使用的时候选择这个 不然会有多个默认的实现 会报错.
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); // 30天
return defaultTokenServices;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/api/all/**").permitAll()
.antMatchers("/api/user/**").fullyAuthenticated()
.antMatchers("/user/**").fullyAuthenticated()
.antMatchers("/api/amc/**").fullyAuthenticated()
.antMatchers("/api/all/**").permitAll()
.antMatchers("/all/**").permitAll()
//you can implement it like this, but I show method invocation security on write
//http.addFilterAfter(new RecordFilter(),SecurityContextHolderAwareRequestFilter.class)
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("biz").tokenStore(tokenStore());
}
}
JedisTokenStore是网上抄来的,主要原因为,redis升级为redis cluster后,原来的RedisTokenStore,不能正常使用了,所以,使用手工编写的版本.
package com.ws.biz
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.security.oauth2.common.OAuth2AccessToken
import org.springframework.security.oauth2.common.OAuth2RefreshToken
import org.springframework.security.oauth2.provider.OAuth2Authentication
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator
import org.springframework.security.oauth2.provider.token.TokenStore
import java.util.concurrent.TimeUnit
/**
* Created by songtao on 2019/3/1.
*/
class JedisTokenStore implements TokenStore{
private static final String ACCESS = "access:";
private static final String AUTH_TO_ACCESS = "auth_to_access:";
private static final String AUTH = "auth:";
private static final String REFRESH_AUTH = "refresh_auth:";
private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
private static final String REFRESH = "refresh:";
private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
private static final String UNAME_TO_ACCESS = "uname_to_access:";
private RedisTemplate redisTemplate ;
public RedisTemplate getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
this.authenticationKeyGenerator = authenticationKeyGenerator;
}
@Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
String key = authenticationKeyGenerator.extractKey(authentication);
OAuth2AccessToken accessToken = (OAuth2AccessToken) redisTemplate.opsForValue().get(AUTH_TO_ACCESS+key);
if (accessToken != null
&& !key.equals(authenticationKeyGenerator.extractKey(readAuthentication(accessToken.getValue())))) {
// Keep the stores consistent (maybe the same user is represented by this authentication but the details
// have changed)
storeAccessToken(accessToken, authentication);
}
return accessToken;
}
@Override
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
return readAuthentication(token.getValue());
}
@Override
public OAuth2Authentication readAuthentication(String token) {
return (OAuth2Authentication) this.redisTemplate.opsForValue().get(AUTH + token);
}
@Override
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return readAuthenticationForRefreshToken(token.getValue());
}
public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
return (OAuth2Authentication) this.redisTemplate.opsForValue().get( REFRESH_AUTH+token);
}
@Override
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
this.redisTemplate.opsForValue().set(ACCESS+ token.getValue(), token);
this.redisTemplate.opsForValue().set(AUTH +token.getValue(), authentication);
this.redisTemplate.opsForValue().set(AUTH_TO_ACCESS+authenticationKeyGenerator.extractKey(authentication), token);
if (!authentication.isClientOnly()) {
redisTemplate.opsForList().rightPush(UNAME_TO_ACCESS+getApprovalKey(authentication), token) ;
}
redisTemplate.opsForList().rightPush(CLIENT_ID_TO_ACCESS+authentication.getOAuth2Request().getClientId(), token) ;
if (token.getExpiration() != null) {
int seconds = token.getExpiresIn();
redisTemplate.expire(ACCESS+ token.getValue(), seconds, TimeUnit.SECONDS) ;
redisTemplate.expire(AUTH+ token.getValue(), seconds, TimeUnit.SECONDS) ;
redisTemplate.expire(AUTH_TO_ACCESS+ authenticationKeyGenerator.extractKey(authentication), seconds, TimeUnit.SECONDS) ;
redisTemplate.expire(CLIENT_ID_TO_ACCESS+authentication.getOAuth2Request().getClientId(), seconds, TimeUnit.SECONDS) ;
redisTemplate.expire(UNAME_TO_ACCESS+ getApprovalKey(authentication), seconds, TimeUnit.SECONDS) ;
}
if (token.getRefreshToken() != null && token.getRefreshToken().getValue() != null) {
this.redisTemplate.opsForValue().set( REFRESH_TO_ACCESS+ token.getRefreshToken().getValue(), token.getValue());
this.redisTemplate.opsForValue().set(ACCESS_TO_REFRESH+token.getValue(), token.getRefreshToken().getValue());
}
}
private String getApprovalKey(OAuth2Authentication authentication) {
String userName = authentication.getUserAuthentication() == null ? "" : authentication.getUserAuthentication()
.getName();
return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
}
private String getApprovalKey(String clientId, String userName) {
return clientId + (userName==null ? "" : ":" + userName);
}
@Override
public void removeAccessToken(OAuth2AccessToken accessToken) {
removeAccessToken(accessToken.getValue());
}
@Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
return (OAuth2AccessToken) this.redisTemplate.opsForValue().get(ACCESS+tokenValue);
}
public void removeAccessToken(String tokenValue) {
OAuth2AccessToken removed = (OAuth2AccessToken) redisTemplate.opsForValue().get(ACCESS+tokenValue);
// Don't remove the refresh token - it's up to the caller to do that
OAuth2Authentication authentication = (OAuth2Authentication) this.redisTemplate.opsForValue().get(AUTH+tokenValue);
this.redisTemplate.delete(AUTH+tokenValue);
redisTemplate.delete(ACCESS+tokenValue);
this.redisTemplate.delete(ACCESS_TO_REFRESH +tokenValue);
if (authentication != null) {
this.redisTemplate.delete(AUTH_TO_ACCESS+authenticationKeyGenerator.extractKey(authentication));
String clientId = authentication.getOAuth2Request().getClientId();
// redisTemplate.opsForList().rightPush("UNAME_TO_ACCESS:"+getApprovalKey(authentication), token) ;
redisTemplate.opsForList().leftPop(UNAME_TO_ACCESS+getApprovalKey(clientId, authentication.getName()));
redisTemplate.opsForList().leftPop(CLIENT_ID_TO_ACCESS+clientId);
this.redisTemplate.delete(AUTH_TO_ACCESS+authenticationKeyGenerator.extractKey(authentication));
}
}
@Override
public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
this.redisTemplate.opsForValue().set(REFRESH+refreshToken.getValue(), refreshToken);
this.redisTemplate.opsForValue().set( REFRESH_AUTH + refreshToken.getValue(), authentication);
}
@Override
public OAuth2RefreshToken readRefreshToken(String tokenValue) {
return (OAuth2RefreshToken) this.redisTemplate.opsForValue().get(REFRESH+tokenValue);
}
@Override
public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
removeRefreshToken(refreshToken.getValue());
}
public void removeRefreshToken(String tokenValue) {
this.redisTemplate.delete( REFRESH + tokenValue);
this.redisTemplate.delete( REFRESH_AUTH + tokenValue);
this.redisTemplate.delete(REFRESH_TO_ACCESS +tokenValue);
}
@Override
public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
removeAccessTokenUsingRefreshToken(refreshToken.getValue());
}
void removeAccessTokenUsingRefreshToken(String refreshToken) {
String token = (String) this.redisTemplate.opsForValue().get( REFRESH_TO_ACCESS +refreshToken) ;
if (token != null) {
redisTemplate.delete(ACCESS+ token);
}
}
@Override
public Collection findTokensByClientIdAndUserName(String clientId, String userName) {
List
现在开始配置网关Zuul的配置
more application.properties
server.port=8080
spring.application.name=zuul
security.oauth2.client.client-id=dw
security.oauth2.client.client-secret=dwsecret
security.oauth2.resource.id=dw
security.oauth2.resource.token-info-uri=http://localhost:8080/oauth/check_token
security.oauth2.authorization.check-token-access=http://localhost:8080/oauth/check_token
security.oauth2.resource.prefer-token-info=true
logging.level.org.springframework.security=DEBUG
spring_scurity_token_access_url=http://localhost:8080/oauth/token
#security.basic.enabled=true
#spring.redis.database=0
#spring.redis.cluster.nodes=10.20.100.228:6376,10.20.100.228:6377,10.20.100.228:6378
#spring.redis.cluster.max-redirects=3
#spring.redis.jedis.pool.max-active=50
#spring.redis.jedis.pool.max-idle=10
#spring.redis.jedis.pool.min-idle=1
#spring.redis.jedis.pool.max-wait=3000
#spring.redis.port=6377
spring.redis.port=6379
spring.redis.database=0
spring.redis.host=10.20.100.235
#spring.redis.pool.max-active=50
#spring.redis.pool.max-idle=10
#spring.redis.pool.max-wait=3000
#spring.redis.pool.min-idle=3
grails.mongodb.url=mongodb://localhost:27017/wensheng
grails.mongodb.engine=codec
grails.mongodb.options.autoConnectRetry=true
grails.mongodb.options.connectTimeout=30000
spring.data.mongodb.uri=mongodb://localhost:27017/wensheng
### actuator##
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true
management.endpoint.health.show-details=always
### zuul
#zuul.prefix=/api
zuul.routes.biz.service-id=biz
zuul.routes.biz.path=/api/**
zuul.routes..biz.strip-prefix=false
#zuul.routes.biz.path=http://localhost:8081/
zuul.routes.biz.custom-sensitive-headers=true
#开始写的是不带引号的模式,一直不行,后来加上了双引号,才能正常的将header传到下游去
#zuul.routes.biz.sensitive-headers=Cookie,Set-Cookie,Content-Range,Authorization
zuul.routes.biz.sensitive-headers="Cookie","Set-Cookie","Content-Range","Authorization"
#zuul.add-host-header=true##实测 暂时没什么用 不能将header转到下游
#zuul.add-proxy-headers=true#这个也没用,不能将header传到下游去
#zuul.ignore-security-headers=true#这个网上说可以将下游的信息带过来,不能传下去,没测怎么带信息过来.
###eureka
eureka.client.allow-redirects=true
eureka.client.enabled=true
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://localhost:7999/eureka/
然后配置AuthorizationServer
package com.ws.zuul
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.ProviderManager
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
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.token.DefaultTokenServices
import org.springframework.security.oauth2.provider.token.TokenStore
import java.util.concurrent.TimeUnit
@Configuration
@EnableAuthorizationServer
class AuthServerConfig extends AuthorizationServerConfigurerAdapter{
@Autowired
private AuthProvider authProvider;
@Bean
public AuthenticationManager authenticationManager() {
println "authenticationProvider now is : "+authProvider
return new ProviderManager(Arrays.asList(authProvider));
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
RedisTemplate redisTemplate
@Bean
public TokenStore tokenStore() {
return new JedisTokenStore(redisTemplate: redisTemplate)
}
@Bean
@Primary //这里必须定义primary 让resource在使用的时候选择这个 不然会有多个默认的实现 会报错.
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); // 30天
return defaultTokenServices;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager());
// endpoints.authenticationManager(authenticationManager);
endpoints.tokenStore(tokenStore())
// DefaultTokenServices tokenServices = new DefaultTokenServices();
// tokenServices.setTokenStore(tokenStore);
// tokenServices.setSupportRefreshToken(true);
// tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
// tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
// tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); // 30天
endpoints.tokenServices(tokenServices());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
// oauthServer.checkTokenAccess("isAuthenticated()");
oauthServer
.checkTokenAccess("permitAll()")
.tokenKeyAccess("permitAll()")
.allowFormAuthenticationForClients()
// .checkTokenAccess("isAuthenticated()")
// oauthServer.checkTokenAccess("permitAll()");
// oauthServer.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
PasswordEncoder encoder = passwordEncoder()
clients.inMemory()
.withClient("dw").secret(encoder.encode("dwsecret"))
.authorizedGrantTypes("authorization_code","password").authorities("ROLE_USER")
.scopes("app")
.and()
.withClient("peixun").secret(encoder.encode("peixunsecret"))
.authorizedGrantTypes("authorization_code","password").authorities("ROLE_AMC,ROLE_USER,ROLE_ADMIN")
.scopes("app")
.and()
.withClient("biz").secret(encoder.encode("bizsecret"))
.authorizedGrantTypes("authorization_code","password").authorities("ROLE_AMC,ROLE_USER,ROLE_ADMIN")
.scopes("app")
}
}
其中的其他配置类为AuthProvider :
package com.ws.zuul
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.oauth2.provider.OAuth2Authentication
import org.springframework.stereotype.Component
import javax.naming.AuthenticationException
/**
* Created by songtao on 2018/3/26.
*/
@Component
class AuthProvider implements AuthenticationProvider{
@Autowired
@Qualifier("userDetailsService")
UserDetailsService userDetailsService
@Autowired
PasswordEncoder passwordEncoder
@Override
Authentication authenticate(Authentication authentication) throws AuthenticationException {
println " "+authentication.getPrincipal()+" "+authentication.getCredentials()+" ===================================="
def user = userDetailsService.loadUserByUsername(authentication.getPrincipal())
if(!user){
throw new AuthenticationException("no user found")
}
// def flag = passwordEncoder().matches(authentication.getCredentials(),user.password) //这里是原来的简单逻辑
//下面是根据我们自己的业务需求写的认证方式
def flag1 = (passwordEncoder.matches(authentication.getCredentials(),user.password))
def flag2 = false
if(authentication instanceof OAuth2Authentication){ //由微信等第三方登录时,由服务器端产生的authentication是这种形式的
def auth = authentication as OAuth2Authentication
String pass = auth.userAuthentication.getCredentials()
flag2 = pass.equals(user.password)
}
def flag = (flag1 || flag2)
if(!flag){
throw new AuthenticationException("password error")
}
// return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), user.authorities);
return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), user.password, user.authorities);
}
@Override
boolean supports(Class> authentication) {
return true
}
}
用户的认证的UserDetailService
package com.ws.zuul
import com.ws.zuul.user.Role
import com.ws.zuul.user.User
import com.ws.zuul.user.oauth.ThirdOauth
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.stereotype.Component
import org.springframework.stereotype.Service
/**
* Created by songtao on 2018/3/26.
*/
@Component
@Service("userDetailsService")
class CustomUserDetailService implements UserDetailsService{
@Override
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = User.findByUsernameOrMobileNumber(username,username)
if(!user){
def third = ThirdOauth.findAllByUnionid(username)
if(third.size()==0){
throw new UsernameNotFoundException("user not found",username)
}else{
user = third.user
}
}
System.out.println("User : "+user);
if(user==null){
System.out.println("User not found");
throw new UsernameNotFoundException("Username not found");
}
return new org.springframework.security.core.userdetails.User(user.username, user.password,
user.enabled, !user.accountExpired,!user.passwordExpired,!user.accountLocked, getGrantedAuthorities(user));
}
private List getGrantedAuthorities(User user){
List authorities = new ArrayList();
for(Role aut in user.authorities){
authorities.add(new SimpleGrantedAuthority(aut.authority))
}
System.out.print("authorities :"+authorities);
return authorities;
}
}
以及配置的sessionConfig是为了SpringCloud 的Session共享使用的,暂时还没用到,后续继续添加
package com.ws.zuul
import org.springframework.context.annotation.Configuration
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400)//1天
class SessionConfig {
/**
* 还有一个比较大的坑,使用Spring Session实际是Spring加入了一个Filter,
* 其本质是:对每一个请求的request都会被DelegatingFilterProxy进行了一次封装。
* 那么,在Controller里面拿出的request实际上是封装后的request,因为Session要存在Session,
* 所以调用request.getSession()的时候,实际上拿到是Spring封装后的session ,
* 因此对于request实际上拿到是Spring封装后的request。那么可能会导致Request.getInputStream无法获取到流数据,
* 对于使用raw格式,即非Key,Value参数的提交 会被Spring-Session获取,当contentType为application/x-www-form-urlencoded时,
* 就会被解析成Paramater,从而导致request.getInputStream的数据再获取时为空了。
* 还有就是Controller里如果用到了request.setCharacterEncoding("GBK");
* 设置字符集,这样的请求也无法生效,因为request.setCharacterEncoding只有在request.getSession或者request.getParamater之前使用有效,
* 而由于使用Spring Session加入了filter,所以Controller里的request.setCharacterEncoding这种方式转编码就失效了,
* 解决办法,可以是new String(request.getParameter(key).getBytes(), "GBK"),获取到参数后转码
*
*/
/**
* session的实现5种方式 :
* 1.粘性session -> 服务器(tomcat中存储session)
* 2.session复制 -> tomcat发送到其他的tomcat
* 3.session共享 -> 1.粘性(缓存中备份) 2.非粘性(只在缓存中存储session)
* 4.session持久化到数据库
* 5.terracotta实现session复制 原理是只复制改变的session对应的tomcat的session 是对2的优化.
*/
}
ZuulApplication的运行为:
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
class ZuulApplication {
...
}
以上,就能配置一个Eureka集成Spring Cloud Security 以及Zuul做网关和认证中心的微服务了.