springsecurity03-springsecurity oauth2实现单点登录之-资源服务器(Resource Service)

资源文件简介

文章参考自 https://projects.spring.io/spring-security-oauth/docs/oauth2.html
一个资源服务(可以和授权服务在同一个应用中,当然也可以分离开成为两个不同的应用程序)提供一些受token令牌保护的资源,Spring OAuth提供者是通过Spring Security authentication filter 即验证过滤器来实现的保护,你可以通过 @EnableResourceServer 注解到一个 @Configuration 配置类上,并且必须使用 ResourceServerConfigurer 这个配置对象来进行配置(可以选择继承自 ResourceServerConfigurerAdapter 然后覆写其中的方法,参数就是这个对象的实例),下面是一些可以配置的属性:

  • tokenServices:ResourceServerTokenServices 类的实例,用来实现令牌服务。
  • resourceId:这个资源服务的ID,这个属性是可选的,但是推荐设置并在授权服务中进行验证。
  • 其他的拓展属性例如 tokenExtractor 令牌提取器用来提取请求中的令牌。
  • 请求匹配器,用来设置需要进行保护的资源路径,默认的情况下是受保护资源服务的全部路径。
  • 受保护资源的访问规则,默认的规则是简单的身份验证(plain authenticated)。
  • 其他的自定义权限保护规则通过 HttpSecurity 来进行配置。

@EnableResourceServer 注解自动增加了一个类型为 OAuth2AuthenticationProcessingFilter 的过滤器链,

在XML配置中,使用 标签元素并指定id为一个servlet过滤器就能够手动增加一个标准的过滤器链。

ResourceServerTokenServices 是组成授权服务的另一半,如果你的授权服务和资源服务在同一个应用程序上的话,你可以使用 DefaultTokenServices ,这样的话,你就不用考虑关于实现所有必要的接口的一致性问题,这通常是很困难的。如果你的资源服务器是分离开的,那么你就必须要确保能够有匹配授权服务提供的 ResourceServerTokenServices,它知道如何对令牌进行解码。
在授权服务器上,你通常可以使用 DefaultTokenServices 并且选择一些主要的表达式通过 TokenStore(后端存储或者本地编码)。
RemoteTokenServices 可以作为一个替代,它将允许资源服务器通过HTTP请求来解码令牌(也就是授权服务的 /oauth/check_token 端点)。如果你的资源服务没有太大的访问量的话,那么使用RemoteTokenServices 将会很方便(所有受保护的资源请求都将请求一次授权服务用以检验token值),或者你可以通过缓存来保存每一个token验证的结果。
使用授权服务的 /oauth/check_token 端点你需要将这个端点暴露出去,以便资源服务可以进行访问,这在咱们授权服务配置中已经提到了,下面是一个例子:

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
    oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')")
        .checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}

在这个例子中,我们配置了 /oauth/check_token 和 /oauth/token_key 这两个端点(受信任的资源服务能够获取到公有密匙,这是为了验证JWT令牌)。这两个端点使用了HTTP Basic Authentication 即HTTP基本身份验证,使用 client_credentials 授权模式可以做到这一点。

配置OAuth-Aware表达式处理器(OAuth-Aware Expression Handler):
你也许希望使用 Spring Security’s expression-based access control 来获得一些优势,一个表达式处理器会被注册到默认的 @EnableResourceServer 配置中,这个表达式包含了 #oauth2.clientHasRole,#oauth2.clientHasAnyRole 以及 #oauth2.denyClient 所提供的方法来帮助你使用权限角色相关的功能(在 OAuth2SecurityExpressionMethods 中有完整的列表)。
在XML配置中你可以注册一个 OAuth-Aware 表达式处理器即 元素标签到 常规的 安全配置上。

资源文件实战

使用之前的授权服务器: https://blog.csdn.net/liaomin416100569/article/details/88529127#_134

授权服务器改造

由于之前在授权服务器使用了jwtstore,使用资源服务器验证时发现JwtHelper类的验证token
有个SignatureVerifier发现如何不使用RSA(公私密钥)verifier永远是空,永远是400 null错误

public static Jwt decodeAndVerify(String token, SignatureVerifier verifier) {
		Jwt jwt = decode(token);
		jwt.verifySignature(verifier);

		return jwt;
	}

生成公私密钥(注意一定要添加–keyalg RSA 默认是DSA哦,JWT只支持RSA)

C:\Users\Administrator>keytool -genkeypair --keyalg RSA  -keystore c:/a.keystore -alias test1
输入密钥库口令:
您的名字与姓氏是什么?
  [Unknown]:  a
您的组织单位名称是什么?
  [Unknown]:  a
您的组织名称是什么?
  [Unknown]:  a
您所在的城市或区域名称是什么?
  [Unknown]:  a
您所在的省/市/自治区名称是什么?
  [Unknown]:  a
该单位的双字母国家/地区代码是什么?
  [Unknown]:  a
CN=a, OU=a, O=a, L=a, ST=a, C=a是否正确?
  [否]:  y

输入 <test1> 的密钥口令
        (如果和密钥库口令相同, 按回车):

将生成的c:/a.keystore拷贝到授权项目的src/main/resources目录
改造授权服务器配置方法

@Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);



        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //一定要设置对称的SigningKey 用于验证之后的token是否有效的
        jwtAccessTokenConverter.setSigningKey("123456");
        KeyStore keyStore = KeyStore.getInstance("JCEKS");
        keyStore.load(OAuth2AuthorizationServerConfig.class.getResourceAsStream("/a.keystore"),"123456".toCharArray());
        PublicKey publicKey = keyStore.getCertificate("test1").getPublicKey();
        PrivateKey privateKey = (PrivateKey)keyStore.getKey("test1", "123456".toCharArray());
        KeyPair kp=new KeyPair(publicKey,privateKey);
        jwtAccessTokenConverter.setKeyPair(kp);
        JwtTokenStore jwtTokenStore = new JwtTokenStore(jwtAccessTokenConverter);
        endpoints.pathMapping("/oauth/confirm_access ","/extenal/oauth/confirm_access")

                .tokenStore(jwtTokenStore)
                .tokenEnhancer(jwtAccessTokenConverter)

                //添加额外信息到token中,注意jwtAccessTokenConverter不能设置额外的tokenEnhancer否则无法生成jwt的token注释掉
//                .tokenEnhancer(new TokenEnhancer() {
//                    @Override
//                    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
//                        DefaultOAuth2AccessToken doat= (DefaultOAuth2AccessToken) oAuth2AccessToken;
//                        Map<String, Object> additionalInfo = new HashMap<>();
//                        additionalInfo.put("myname", "jiaozi");
//                        doat.setAdditionalInformation(additionalInfo);
//                        return doat;
//                    }
//                })
        ;
    }

资源服务器改造

新建maven项目
添加依赖(和资源服务器一致)

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.8.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <version>1.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.6.2</version>
        </dependency>
    </dependencies>

添加资源服务器配置类OAuth2ResourceServerConfig

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        super.configure(http);
    }

    public RestTemplate restTemplate() {
        //httpRequestFactory()
        RestTemplate restTemplate = new RestTemplate();
        List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                MappingJackson2HttpMessageConverter jsonConverter = (MappingJackson2HttpMessageConverter) converter;
                jsonConverter.setObjectMapper(new ObjectMapper());
                List<MediaType> supportedMediaTypes=new ArrayList<>();
                supportedMediaTypes.add(new MediaType("application", "json", MappingJackson2HttpMessageConverter.DEFAULT_CHARSET));
                supportedMediaTypes.add(new MediaType("text", "javascript", MappingJackson2HttpMessageConverter.DEFAULT_CHARSET));
                jsonConverter.setSupportedMediaTypes(supportedMediaTypes);
            }
        }
        return restTemplate;
    }
    /**
     因为资源服务器和授权服务器分离所以使用RemoteTokenServices
   */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        super.configure(resources);
        RemoteTokenServices remoteTokenServices=new RemoteTokenServices();
        remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:8889/oauth/check_token");//指定授权服务器检查token的地址
        remoteTokenServices.setClientId("client");
        remoteTokenServices.setClientSecret("secret");
        remoteTokenServices.setRestTemplate(restTemplate());
        remoteTokenServices.setAccessTokenConverter(new JwtAccessTokenConverter());
        resources.tokenServices(remoteTokenServices);
    }
}

配置application.properties

server.port=8887
spring.thymeleaf.cache=false
spring.thymeleaf.mode=LEGACYHTML5

添加启动类

@SpringBootApplication
public class ResourceServerMain {
    public static void main(String[] args) {
        SpringApplication.run(ResourceServerMain.class);
    }
}

添加一个控制层的资源

@Controller
public class TestController {
    @ResponseBody
    @GetMapping("/test")
    public String test() {
        return "hello";
    }
}

开始测试(前面两步过程参考授权服务器文章)

  1. 访问认证服务器获取授权码
    localhost:8889/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://www.baidu.com
    拷贝授权码 假设是Ce6BxM
  2. 通过授权码获取token
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTI1OTk1MDEsInVzZXJfbmFtZSI6InRlc3QiLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiNjQxNzA0MTEtYTQ3Ni00NzQ5LWIzZTItOTM2NzA0NmY5MjAzIiwiY2xpZW50X2lkIjoiY2xpZW50Iiwic2NvcGUiOlsiYWxsIl19.XERRnoc-NVXJ2H6wweDK2hl9Wf7gI48NHY0aMXh2g16Hctwv60-wn41FcQnOVuSxChbBe7oe5kaXQdq7SjbTMbjAD0VPu6B4X18IsTgJ5BP-tGruhWdtxcqCJ_Gg8HRkI-62F_RO7n-B1zkf-ZmvSPO3chvBL7xiH8S0lE0c1b5FXMMIFtoqvSPVPNAt9UDo6p4JiGWwKq9Podo1bH9FxOxoHrVBYb03IRn_ASjd0Vx0iQZVG-J6VTDDUfHcHPc1HwPgaK7aEoaE-9WucsgtXDaa-C69PcU-XHPwxfKDAYrgTIAJ1kux0DLuMjWavALho1BxlVeWImFe3b0WPhKooA","token_type":"bearer","expires_in":43199,"scope":"all","jti":"64170411-a476-4749-b3e2-9367046f9203"}
  1. 通过授权码访问/test
    注意访问的过程是在访问的请求头 加上
    Authorization: Bearer 你的token

在这里插入图片描述
我图里面用的post请求

{"timestamp":"2019-03-14T09:40:59.035+0000","status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/test"}

我的控制层是get请求使用get请求成功输出hello
在这里插入图片描述
你也可以直接在浏览器输入check_token来检查token是否正确
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值