security-oauth2 (二)

授权服务器可参考另一篇文章

搭建资源服务器

创建一个新的子模块client

介绍

        @EnableResourceServer 注解到一个 @Confifiguration 配置类上,并且必须使用 ResourceServerConfifigurer 这个配置对象来进行配置(可以选择继承自ResourceServerConfifigurerAdapter 然后覆写其中的方法,参数就是这个对象的实例),下面是一些可以配置的属性:

        ResourceServerSecurityConfifigurer中主要包括:

  • tokenServicesResourceServerTokenServices 类的实例,用来实现令牌服务。
  • tokenStoreTokenStore类的实例,指定令牌如何访问,与tokenServices配置可选
  • resourceId:这个资源服务的ID,这个属性是可选的,但是推荐设置并在授权服务中进行验证。
  • 其他的拓展属性例如 tokenExtractor 令牌提取器用来提取请求中的令牌。
HttpSecurity 配置这个与 Spring Security 类似:
  • 请求匹配器,用来设置需要进行保护的资源路径,默认的情况下是保护资源服务的全部路径。
  • 通过http.authorizeRequests()来设置受保护资源的访问规则
  • 其他的自定义权限保护规则通过 HttpSecurity 来进行配置。
@EnableResourceServer 注解自动增加了一个类型为 OAuth2AuthenticationProcessingFilter 的过滤器链
编写 ResouceServerConfifig
package com.security.oauth2.oauth2client.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    public static final String RESOURCE_ID = "res1";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources
                //资源id
                .resourceId(RESOURCE_ID)
                //验证令牌的服务,远程验证
                .tokenServices(tokenService())
                //无状态
                .stateless(true);
    }
    //资源服务令牌解析服务,会向授权服务器发送请求验证令牌的正确性
    @Bean
    public ResourceServerTokenServices tokenService() {
        //使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
        RemoteTokenServices service=new RemoteTokenServices();
        service.setCheckTokenEndpointUrl("http://localhost:8081/demo2/oauth/check_token");
        service.setClientId("c1");
        service.setClientSecret("secret");
        return service;
    }


    @Override
    public void configure(HttpSecurity http) throws Exception {
        http .authorizeRequests()
                //如果令牌中的授权是‘ROLE_ADMIN’,则可以访问任意路径
                .antMatchers("/**").access("#oauth2.hasScope('ROLE_ADMIN')")
                .and()
                //跨域
                .csrf().disable()
                .sessionManagement()
                //关闭session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

}

验证token

ResourceServerTokenServices 是组成授权服务的另一半,如果你的授权服务和资源服务在同一个应用程序上的话,你可以使用 DefaultTokenServices ,这样的话,你就不用考虑关于实现所有必要的接口的一致性问题。如果你的资源服务器是分离开的,那么你就必须要确保能够有匹配授权服务提供的 ResourceServerTokenServices ,它知道如何对令牌进行解码。
令牌解析方法: 使用 DefaultTokenServices 在资源服务器本地配置令牌存储、解码、解析方式 使用 RemoteTokenServices 资源服务器通过 HTTP 请求来解码令牌,每次都请求授权服务器端点 /oauth/check_token 使用授权服务的 /oauth/check_token 端点你需要在授权服务将这个端点暴露出去,以便资源服务可以进行访问,
这在咱们授权服务配置中已经提到了,下面是一个例子 , 在这个例子中,我们在授权服务中配置了
/oauth/check_token /oauth/token_key 这两个端点:
@Override 
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { 
    security 
             .tokenKeyAccess("permitAll()")// /oauth/token_key 安全配置
             .checkTokenAccess("permitAll()") // /oauth/check_token 安全配置 
}
在资源 服务配置 RemoteTokenServices ,在 ResouceServerConfifig 中配置:
    public static final String RESOURCE_ID = "res1";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources
                //资源id
                .resourceId(RESOURCE_ID)
                //验证令牌的服务,远程验证
                .tokenServices(tokenService())
                //无状态
                .stateless(true);
    }
    //资源服务令牌解析服务,会向授权服务器发送请求验证令牌的正确性
    @Bean
    public ResourceServerTokenServices tokenService() {
        //使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
        RemoteTokenServices service=new RemoteTokenServices();
        service.setCheckTokenEndpointUrl("http://localhost:8081/demo2/oauth/check_token");
        service.setClientId("c1");
        service.setClientSecret("secret");
        return service;
    }
编写资源
controller 包下编写 OrderController ,此 controller 表示订单资源的访问类
package com.security.oauth2.oauth2client.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ClientController {

    @GetMapping("/r/r1")
    public String r1(){
        return "访问资源1";
    }


    @GetMapping("/r/r2")
    public String r2(){
        return "访问资源2";
    }
}

启动访问:

获取令牌

按照 oauth2.0 协议要求,请求资源需要携带 token ,如下:
token 的参数名称为: Authorization ,值为: Bearer token
访问r1:
访问r2

 

添加资源控制 

package com.security.oauth2.oauth2client.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //安全拦截机制(最重要)
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
    // .antMatchers("/r/r1").hasAuthority("p2")
    // .antMatchers("/r/r2").hasAuthority("p2")
                .antMatchers("/r/**").authenticated()//所有/r/**的请求必须认证通过
                .anyRequest().permitAll();//除了/r/**,其它的请求可以访问 ;
    }

}

 修改controller增加权限限制

package com.security.oauth2.oauth2client.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ClientController {

    @GetMapping("/r/r1")
    @PreAuthorize("hasAnyAuthority('p1')")
    public String r1(){
        return "访问资源1";
    }


    @GetMapping("/r/r2")
    @PreAuthorize("hasAnyAuthority('p2')")
    public String r2(){
        return "访问资源2";
    }
}

重新访问

访问 r1

访问 r2

 

 由于现在每次的令牌验证都需要向授权服务器发送请求验证,比较消耗性能,推荐使用jwt令牌验证

jwt令牌:

修改授权服务器配置

修改令牌策略TokenConfig:

package com.security.oauth2.oauth2service.config.oauth2;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

@Configuration
public class TokenConfig {

//    /**
//     * 令牌策略-内存方式
//     * @return
//     */
//    @Bean
//    public TokenStore tokenStore(){
//        //内存方式
//        return new InMemoryTokenStore();
//    }

    //加密盐,防止被篡改
    private String SIGNING_KEY = "uaa123";

    /**
     * 令牌策略-jwt方式
     * @return
     */
    @Bean
    public TokenStore tokenStore(){
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        //对称密匙,资源服务器使用该密匙验证
        converter.setSigningKey(SIGNING_KEY); 
        return converter;
    }
}

在授权服务器中AuthorizationServer配置文件中的令牌配置中 添加以下代码:

        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
        services.setTokenEnhancer(tokenEnhancerChain);

完整配置

package com.security.oauth2.oauth2service.config.oauth2;


import com.security.oauth2.oauth2service.config.security.MD5PasswordEncoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
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.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.Arrays;

/**
 * oauth2
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private DataSource dataSource;

    @Bean
    public ClientDetailsService clientDetails() {
        //使用数据库
        ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        ((JdbcClientDetailsService) clientDetailsService)
                //配置密匙加密为md5
                .setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    /**
     * 配置客户端认证(谁来申请令牌)使用数据库
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());
    }




    //注入令牌策略-内存方式
    @Autowired
    private TokenStore tokenStore;

    //jwt令牌
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    
    /**
     * 配置令牌服务
     */
    @Bean
    public AuthorizationServerTokenServices tokenService(){
        DefaultTokenServices services = new DefaultTokenServices();
        //使用客户端配置
        services.setClientDetailsService(clientDetails());
        //是否使用刷新令牌
        services.setSupportRefreshToken(true);
        //令牌的存储策略
        services.setTokenStore(tokenStore);
        //使用jwt令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
        services.setTokenEnhancer(tokenEnhancerChain);
        //令牌默认有效期2小时
        services.setAccessTokenValiditySeconds(7200);
        //刷新令牌默认有效期3天
        services.setRefreshTokenValiditySeconds(259200);
        return services;
    }

    /**
     * 授权码服务 使用数据库
     * @return
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(){
        //使用数据库
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    /**
     * 在security配置文件WebSecurityConfig 中加入bean容器
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 配置令牌的访问端点(申请令牌的地址)和令牌服务(令牌怎么发放)
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                //认证管理器,密码模式需要
                .authenticationManager(authenticationManager)
                //授权码服务
                .authorizationCodeServices(authorizationCodeServices())
                //令牌管理服务
                .tokenServices(tokenService())
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

    /**
     * 令牌的安全约束(验证有没有资格申请令牌)
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //oauth/token_key是公开
                .tokenKeyAccess("permitAll()")
                //oauth/check_token公开
                .checkTokenAccess("permitAll()")
                //表单认证(申请令牌)
                .allowFormAuthenticationForClients();
    }

}

测试

修改资源服务器

将授权服务器中的令牌策略文件 TokenConfig 复制到资源服务器 

然后修改配置文件ResourceServerConfig 令牌策略:

package com.security.oauth2.oauth2client.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.TokenStore;

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    public static final String RESOURCE_ID = "res1";

    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources
                //资源id
                .resourceId(RESOURCE_ID)
                //验证令牌的服务,jwt令牌
                .tokenStore(tokenStore)
                //无状态
                .stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http .authorizeRequests()
                //如果令牌中的授权是‘ROLE_ADMIN’,则可以访问任意路径
                .antMatchers("/**").access("#oauth2.hasScope('ROLE_ADMIN')")
                .and()
                //跨域
                .csrf().disable()
                .sessionManagement()
                //关闭session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

}

启动测试:

访问 r1:

访问 r2

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值