java代码和性能优化产品运营

认证与授权(三):搭建授权、资源服务器

2020-05-17  本文已影响0人  JBryan

本篇示例的结构如图所示:


授权、资源结构图.jpg
  1. 客户端向授权服务器请求令牌,授权服务器经过认证以后,将令牌返回给客户端。
  2. 客户端携带第1步返回的令牌,向资源服务器请求资源。
  3. 资源服务器向授权服务器验证令牌的合法性和有效性。
  4. 若验证成功,资源服务器向客户端返回受限资源。

1、搭建授权服务器

授权服务器是在上一篇搭建Spring Cloud Security单体应用的基础上完成的。
pom.xml添加依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

Eureka服务注册与发现中心的搭建参考服务注册与发现:Eureka
application.yml添加Eureka Client配置

eureka:
    instance:
        instance-id: ${spring.application.name}:${vcap.application.instance_id:${spring. application.instance_id:${random.value}}}
    client:
        service-url:
            default-zone: http://localhost:8761/eureka/

新建AuthorizationServerConfig

package com.ljessie.controlsecurity.config;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

//开启授权服务器
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig
        extends AuthorizationServerConfigurerAdapter
{

    @Autowired
    AuthenticationManager manager;

    @Autowired
    PasswordEncoder passwordEncoder;

    /**
     * 用来指定哪些客户端可以来请求授权
     * 配置客户端详情信息
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        //暂时使用内存方式
        //配置一个客户端,既可以通过授权码类型获取令牌,也可以通过密码类型获取令牌
        clients.inMemory().withClient("client") //客户端ID
                .authorizedGrantTypes("authorization_code","password", "refresh_token") //客户端可以使用的授权类型
                .scopes("all") //允许请求范围
                .secret(passwordEncoder.encode("secret")) //客户端密钥
                .redirectUris("http://localhost:8768");//回调地址
    }

    /**
     * 令牌访问端点
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(new InMemoryTokenStore())
                .accessTokenConverter(accessTokenConverter())
                .authenticationManager(manager)
                    .reuseRefreshTokens(false);
    }

    /**
     * 配置JWT转换器
     * @return
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("secret");
        return converter;
    }


    /**
     * 令牌端点的安全策略
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //允许所有人请求令牌
                .tokenKeyAccess("permitAll()")
                //已验证的客户端才能请求check_token端点
                .checkTokenAccess("isAuthenticated()")
                //允许表单认证,申请令牌
                .allowFormAuthenticationForClients();
    }
}

2、搭建资源服务器

首先按照服务注册与发现:Eureka中的contro-record搭建Eureka Client
pom.xml然后添加security和oauth依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

新建TestController,配置一个受限资源接口test_resource

package com.ljessie.controluser.controller;

import com.ljessie.controluser.entity.User;
import com.ljessie.controluser.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class TestController {

    @RequestMapping("test_resource")
    public String testResource(){
        return "返回受限资源";
    }

}

新建ResourceServerConfig

package com.ljessie.controluser.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
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.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;


@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Autowired
    RestTemplate restTemplate;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources
                .tokenStore(new JwtTokenStore(accessTokenConverter()))
                .stateless(true);
        //配置RemoteTokenServices,用于向认证服务器验证令牌
        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        remoteTokenServices.setAccessTokenConverter(accessTokenConverter());

        restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
            /**
             * 忽略400异常
             * @param response
             * @param statusCode
             * @throws IOException
             */
            @Override
            protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
                if(response.getRawStatusCode() != 400){
                    super.handleError(response);
                }
            }
        });

        remoteTokenServices.setRestTemplate(restTemplate);
        remoteTokenServices.setCheckTokenEndpointUrl("http://authorizationServer/oauth/check_token");
        remoteTokenServices.setClientId("client");
        remoteTokenServices.setClientSecret("secret");

        resources.tokenServices(remoteTokenServices)
                .stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //配置资源服务器的拦截规则
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and().requestMatchers().anyRequest()
                .and().anonymous()
                //test_resource接口必须经过认证才可以访问
                .and().authorizeRequests().antMatchers("/test_resource").authenticated()
                .and()
                .exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());

    }

    /**
     * 配置JWT转换器
     * @return
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("secret");
        return converter;
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

@EnableResourceServer注解开启资源服务,configure(ResourceServerSecurityConfigurer resources)方法添加授权服务器相关配置,configure(HttpSecurity http)配置了哪些资源需要授权才能访问,accessTokenConverter()配置JWT转换器解析令牌。

3、测试授权

依次启动control-eureka-server、control-security和control-user。
使用postman访问:http://localhost:8762/test_resource,返回未授权。

未授权.jpg
访问请求令牌接口,请求方式POST:http://localhost:8766/oauth/token?username=066111&password=123456&grant_type=password&scope=all&client_id=client&client_secret=secret
返回令牌.jpg
请求令牌的接口是Spring Cloud Security自带的,不需要我们自己手写。
然后复制access_token,再次访问首先资源接口:
返回资源.jpg
上一篇下一篇

猜你喜欢

热点阅读