spring cloud security集成zuul

研究spring cloud security与Zuul集成

需要将请求通过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<String,Object> redisTemplate ;

    public RedisTemplate<String,Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String,Object> 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<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
        List<Object> result =    redisTemplate.opsForList().range(UNAME_TO_ACCESS+ getApprovalKey(clientId, userName), 0, -1);

        if (result == null || result.size() == 0) {
            return Collections.<OAuth2AccessToken> emptySet();
        }
        List<OAuth2AccessToken> accessTokens = new ArrayList<OAuth2AccessToken>(result.size());

        for(Iterator<Object> it = result.iterator();it.hasNext();){
            OAuth2AccessToken accessToken = (OAuth2AccessToken) it.next();
            accessTokens.add(accessToken);
        }

        return Collections.<OAuth2AccessToken> unmodifiableCollection(accessTokens);
    }

    @Override
    public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
        List<Object> result =    redisTemplate.opsForList().range((CLIENT_ID_TO_ACCESS+clientId), 0, -1);

        if (result == null || result.size() == 0) {
            return Collections.<OAuth2AccessToken> emptySet();
        }
        List<OAuth2AccessToken> accessTokens = new ArrayList<OAuth2AccessToken>(result.size());

        for(Iterator<Object> it = result.iterator();it.hasNext();){
            OAuth2AccessToken accessToken = (OAuth2AccessToken) it.next();
            accessTokens.add(accessToken);
        }

        return Collections.<OAuth2AccessToken> unmodifiableCollection(accessTokens);
    }
}

现在开始配置网关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<GrantedAuthority> getGrantedAuthorities(User user){
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

        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做网关和认证中心的微服务了.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值