Spring Cloud Security OAuth2(二):搭建资源服务端

7

该篇文章主要记录,使用spring cloud security oauth2 的一些过程。
关于spring cloud security oauth2一些基本知识参考: Spring Security OAuth 2开发者指南;
编写过程过也参考过其他文章教程,例如:Spring cloud微服务实战——基于OAUTH2.0统一认证授权的微服务基础架构Spring Boot Security OAuth2 例子(Bcrypt Encoder)Spring Security OAuth2实现使用JWT

客户端安全配置
简单设置了一个默认的安全配置
package com.hq.biz.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

/**
 * @author Administrator
 * @Package com.hq.biz.config
 * @Description: GlobalMethodSecurityConfig
 * @date 2018/4/23 15:59
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class GlobalMethodSecurityConfig  {
}
资源配置
package com.hq.biz.config;

import com.hq.biz.handler.CustomAccessDeniedHandler;
import com.hq.biz.handler.CustomAuthEntryPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.boot.autoconfigure.security.oauth2.authserver.AuthorizationServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
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.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.FileCopyUtils;

import java.io.IOException;

/**
 * @author huangqi
 * @Package com.hq.biz.config
 * @Description: ResourceServerConfig
 * @date 2018/6/27 9:39
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    Logger log = LoggerFactory.getLogger(ResourceServerConfig.class);

    @Autowired
    private OAuth2ClientProperties oAuth2ClientProperties;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;


    @Autowired
    private AuthorizationServerProperties authorizationServerProperties;

    @Autowired
    private CustomAuthEntryPoint customAuthEntryPoint;

    @Autowired
    private CustomAccessDeniedHandler customAccessDeniedHandler;

    @Bean
    public AuthorizationServerProperties authorizationServerProperties() {
        return new AuthorizationServerProperties();
    }


    @Bean
    @Qualifier("authorizationHeaderRequestMatcher")
    public RequestMatcher authorizationHeaderRequestMatcher() {
        return new RequestHeaderRequestMatcher("Authorization");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .csrf()
                .disable()
                .exceptionHandling().authenticationEntryPoint(customAuthEntryPoint)
                .and()
                .headers()
                .frameOptions()
                .disable()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .requestMatcher(authorizationHeaderRequestMatcher())
                .authorizeRequests()
                .antMatchers("hq/login","hq/logout").permitAll();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenServices(tokenServices())
                .accessDeniedHandler(customAccessDeniedHandler)
                .authenticationEntryPoint(customAuthEntryPoint);


    }

    @Bean
    protected JwtAccessTokenConverter jwtTokenEnhancer() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        Resource resource = new ClassPathResource("hq-jwt.cert");
        String publicKey;
        try {
            publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        converter.setVerifierKey(publicKey);
        return converter;
    }

    @Bean
    public TokenStore redisTokenStore() {
        return new RedisTokenStore(redisConnectionFactory);
    }


    @Bean
    public ResourceServerTokenServices tokenServices() {
        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        remoteTokenServices.setCheckTokenEndpointUrl(authorizationServerProperties.getCheckTokenAccess());
        remoteTokenServices.setClientId(oAuth2ClientProperties.getClientId());
        remoteTokenServices.setClientSecret(oAuth2ClientProperties.getClientSecret());
        remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
        return remoteTokenServices;
    }

    @Bean
    public AccessTokenConverter accessTokenConverter() {
        return new DefaultAccessTokenConverter();
    }

}
登录

这边我使用RestTemplate 构造token请求获取token;以及使用feign调用认证服务器端删除token方法注销登录


package com.hq.biz.controller;

import com.hq.biz.dto.UserDTO;
import com.hq.biz.entity.Result;
import com.hq.biz.enums.ResultEnum;
import com.hq.biz.feign.OAuth2ServerClient;
import com.hq.biz.feign.UserClient;
import com.hq.biz.utils.BCryptUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.http.*;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Administrator
 * @Package com.hq.biz
 * @Description: ${TODO}(用一句话描述该文件做什么)
 * @date 2018/5/4 10:26
 */
@RestController
@RequestMapping("/hq")
public class LoginController {
    public static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private OAuth2ClientProperties oAuth2ClientProperties;
    @Autowired
    private OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private UserClient userClient;

    @Autowired
    private OAuth2ServerClient oAuth2ServerClient;

    @Autowired
    @Qualifier("redisTokenStore")
    private TokenStore tokenStore;

    /**
     * 通过密码授权方式向授权服务器获取令牌
     *
     * @return
     * @throws Exception
     */
    @PostMapping(value = "/login")
    public Result login(@RequestParam("userName") String userName, @RequestParam("passWord") String passWord) throws Exception {
        //验证用户名密码
        Result<UserDTO> userDTOResult = userClient.queryByName(userName);
        if (!ResultEnum.SUCCESS.getCode().equals(userDTOResult.getRespCode())) {
            return new Result(ResultEnum.LOGIN_FAIL);
        }
        UserDTO respData = userDTOResult.getRespData();
        if(BCryptUtil.isMatch(passWord,respData.getPassWord())){
            ResponseEntity<OAuth2AccessToken> responseEntity = getToken(userName, passWord);
            if (HttpStatus.OK.equals(responseEntity.getStatusCode())) {
                DefaultOAuth2AccessToken body = (DefaultOAuth2AccessToken) responseEntity.getBody();
                Map<String, String> tkMap = new HashMap<>(5);
                tkMap.put("access_token", body.getValue());
                tkMap.put("refresh_token", body.getRefreshToken().getValue());
                return Result.returnOk(tkMap);
            } else {
                return new Result(ResultEnum.LOGIN_FAIL);
            }
        }else {
            return new Result(ResultEnum.LOGIN_USER_ERR);
        }
    }

    @RequestMapping("/logout")
    public Result exit(HttpServletRequest request) {
        String authHeader = request.getHeader("Authorization");
        String tokenValue = authHeader.replace("bearer", "").trim();
        Result result = oAuth2ServerClient.removeToken(tokenValue);
        if (!ResultEnum.SUCCESS.getCode().equals(result.getRespCode())) {
            return new Result(ResultEnum.LOGOUT_FAIL);
        }

        return new Result(ResultEnum.SUCCESS.getCode(), "注销成功");
    }

    @PostMapping(value = "/hello")
    public Result hello() throws Exception {

        return Result.returnOk("hello");
    }

    public ResponseEntity<OAuth2AccessToken> getToken(String userName, String passWord) {
        //Http Basic 验证
        String clientAndSecret = oAuth2ClientProperties.getClientId() + ":" + oAuth2ClientProperties.getClientSecret();
        //这里需要注意为 Basic 而非 Bearer
        clientAndSecret = "Basic " + Base64.getEncoder().encodeToString(clientAndSecret.getBytes());
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.set("Authorization", clientAndSecret);
        //授权请求信息
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.put("username", Collections.singletonList(userName));
        map.put("password", Collections.singletonList(passWord));
        map.put("grant_type", Collections.singletonList(oAuth2ProtectedResourceDetails.getGrantType()));
        map.put("scope", oAuth2ProtectedResourceDetails.getScope());
        //HttpEntity
        HttpEntity httpEntity = new HttpEntity(map, httpHeaders);
        //获取 Token
        ResponseEntity<OAuth2AccessToken> exchange = restTemplate.exchange(oAuth2ProtectedResourceDetails.getAccessTokenUri(), HttpMethod.POST, httpEntity, OAuth2AccessToken.class);
        return exchange;
    }

}
总配置
package com.hq.biz;

import com.hq.biz.filter.PreRequestZuulFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@EnableZuulProxy
@SpringBootApplication
@EnableFeignClients
public class CloudGatewayServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(CloudGatewayServerApplication.class, args);
    }

    @Bean
    public PreRequestZuulFilter preRequestZuulFilter(){
        return new PreRequestZuulFilter();
    }
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
配置文件
server:
  port: 9999

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8888/eureka/
logging:
  level:
    com.hq.*: debug

spring:
  application:
    name: biz-server


zuul:
  routes:
    api-a:
      path: /api-a/**
      serviceId: cloud-user


security:
  oauth2:
    client:
      clientId: hq
      clientSecret: hq
      userAuthorizationUri: http://localhost:8000/oauth/authorize
      grant-type: password
      scope: xx
      access-token-uri: http://localhost:8000/oauth/token
    resource:
      userInfoUri: http://localhost:8000/user
    authorization:
      check-token-access: http://localhost:8000/oauth/check_token


feign:
  hystrix:
    enabled: true


你可能感兴趣的

载入中...