逐步深入构建springcloud oauth2 内存,jwt,数据库,动态权限,第三方登录(微信等)

原创
01/28 17:13
阅读数 117

 

最近公司内部有个项目,突然做了安全测试结果提了一堆改进意见,大概就是越权访问。这根之前项目周期和项目人员安排有关,周期短,人员少,经验不足。导致整个项目仅做了普通的登录认证,没有做到详细的权限管理。领导安排我根据测试报告提出改进策略。其实这种东西尤其是管理端,最好不要外网访问,通过rbac权限模型闲置用户功能,数据,接口等权限。好多年前没有shrio,springsecurity公司都是自己在filter中自己写逻辑限制。现在有了shrio,springsecurity这种框架,大大减少了开发人员的工作量。但是shrio和springsecurity这种框架,只是做到了大范围的权限限制,动态的权限变化,组织机构等变化导致的用户权限范围变化没有直接实现,就需要我们自己动手去改。大家修改的方案各不相同,我就先学学springsecurity逐步的实现动态权限变化。

一、开发环境及工具

  1. MacPro
  2. java8
  3. idea 2020
  4. springcloud  Greenwich.SR2

二、逐步了解实现

  1. 简单搭建springsecurity oauth2,基于内存 ,主要是如何配置
    1. pom配置

        

<dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
   <!-- <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>-->
  </dependencies>

授权服务配置


@Configuration
@EnableAuthorizationServer
public class AuthConfig extends AuthorizationServerConfigurerAdapter {

    @Bean
    BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    TokenStore tokenStore;
//
    @Autowired
    ClientDetailsService clientDetailsService;

//    @Autowired
//    JwtAccessTokenConverter accessTokenConverter;

    @Autowired
    AuthorizationCodeServices authorizationCodeServices;



    public AuthorizationServerTokenServices tokenServices(){
        DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
        defaultTokenServices.setClientDetailsService(clientDetailsService);
        defaultTokenServices.setTokenStore(tokenStore);
        defaultTokenServices.setReuseRefreshToken(true);
//        //令牌增强
//        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
//        tokenEnhancerChain.setTokenEnhancers(Arrays.<TokenEnhancer>asList(accessTokenConverter));
//        defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);


        defaultTokenServices.setAccessTokenValiditySeconds(7200);
        defaultTokenServices.setRefreshTokenValiditySeconds(259200);
        return defaultTokenServices;
    }
    public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception{
        clientDetailsServiceConfigurer
                .inMemory().withClient("client").
                secret(passwordEncoder().encode("secret"))
                .authorizedGrantTypes("authorization_code")
                .scopes("app")
                .redirectUris("http://www.baidu.com");

    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() { //设置授权码模式的授权码如何
        return new InMemoryAuthorizationCodeServices();
    }
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
       security.tokenKeyAccess("permitAll()")
               .checkTokenAccess("permitAll()")
               .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        super.configure(endpoints);
        endpoints.authenticationManager(authenticationManager)
                .authorizationCodeServices(authorizationCodeServices)
                .tokenServices(tokenServices())
        .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }
}

 上面代码主要是实现父类的三个configre。

tokenstore的配置

@Configuration
public class TockenConfig {
    private static  final String SIGNING_KEY = "signing_key";
//    @Bean
//    public TokenStore tokenStore(){
//        return new JwtTokenStore(this.accessTokenConverter());
//    }
//
//    @Bean
//    public JwtAccessTokenConverter accessTokenConverter() {
//        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
//        converter.setVerifierKey(SIGNING_KEY);
//        converter.setSigningKey(SIGNING_KEY);
//        return converter;
//    }

//  内存形式
    @Bean
    public TokenStore tokenStore(){
        return new InMemoryTokenStore();
    }
}

这步使用内存存储平通的token

security安全方面,用户及密码内存的配置


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    BCryptPasswordEncoder bCryptPasswordEncoder;

    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.inMemoryAuthentication()
                .withUser("admin").password(bCryptPasswordEncoder.encode("123456")).roles("ADMIN").and()
                .withUser("user").password(bCryptPasswordEncoder.encode("123456")).roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/r/r1").hasAnyAuthority("p1")
                .antMatchers("/login*").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin();
    }

}

这样基本就配置完了。

测试

1.获取授权码

http://localhost:8088/oauth/authorize?client_id=client&response_type=code&scope=app&redirect_uri=http://www.baidu.com

这步会跳到登录页面

2.输入内存配置的用户密码跳转到授权确认页

3.点击approve,跳转到redirect_url页面,并带有授权码

4.获取授权码,以form表单提交

http://localhost:8088/oauth/token

这样基于内存的就测试完了。

2.为了减少资源服务访问授权的服务量,引如jwt,jwt的介绍网上一大堆。到此还没有引入数据库。

    修改tokenstore配置,增加jwttokenconverter

@Configuration
public class TockenConfig {
    private static  final String SIGNING_KEY = "signing_key";
    @Bean
    public TokenStore tokenStore(){
        return new JwtTokenStore(this.accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setVerifierKey(SIGNING_KEY);
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }
    //内存形式
//    @Bean
//    public TokenStore tokenStore(){
//        return new InMemoryTokenStore();
//    }
}

授权服务中,加入令牌增强。

@Configuration
@EnableAuthorizationServer
public class AuthConfig extends AuthorizationServerConfigurerAdapter {

    @Bean
    BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    TokenStore tokenStore;
//
    @Autowired
    ClientDetailsService clientDetailsService;

    @Autowired
    JwtAccessTokenConverter accessTokenConverter;

    @Autowired
    AuthorizationCodeServices authorizationCodeServices;



    public AuthorizationServerTokenServices tokenServices(){
        DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
        defaultTokenServices.setClientDetailsService(clientDetailsService);
        defaultTokenServices.setTokenStore(tokenStore);
        defaultTokenServices.setReuseRefreshToken(true);
        //令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.<TokenEnhancer>asList(accessTokenConverter));
        defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);


        defaultTokenServices.setAccessTokenValiditySeconds(7200);
        defaultTokenServices.setRefreshTokenValiditySeconds(259200);
        return defaultTokenServices;
    }
    public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception{
        clientDetailsServiceConfigurer
                .inMemory().withClient("client").
                secret(passwordEncoder().encode("secret"))
                .authorizedGrantTypes("authorization_code")
                .scopes("app")
                .redirectUris("http://www.baidu.com");

    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() { //设置授权码模式的授权码如何
        return new InMemoryAuthorizationCodeServices();
    }
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
       security.tokenKeyAccess("permitAll()")
               .checkTokenAccess("permitAll()")
               .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        super.configure(endpoints);
        endpoints.authenticationManager(authenticationManager)
                .authorizationCodeServices(authorizationCodeServices)
                .tokenServices(tokenServices())
        .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }
}

security的配置不变,配置完了

开启测试

1.授权访问和上面一样

2.获取令牌和上面一样,但是返回的令牌内容不一样,加入jwt增强后明显token长度边长,返回内容增加

3.接下来加入jdbc 存储client相关信息和用户的信息。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部