spring-cloud-starter-oauth2+securit+jwt微服务实战 | 青训营笔记

139 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天

spring-cloud-starter-oauth2+securit简介

认证
  • 用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信 息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录,二维 码登录,手机短信登录,指纹认证等方式。

认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。

授权

  • 授权是用户认证通过后,根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访 问,没有权限则拒绝访问。

认证是为了保证用户身份的合法性,授权则是为了更细粒度的对隐私数据进行划分,授权是在认证通过 后发生的, 控制不同的用户能够访问不同的资源。

RBAC模型

主体 -》 角色 -》 资源 -》行为

image.png

spring-cloud-starter-oauth2+security使用

相关依赖引入

  • spring-cloud-starter-oauth2+security是一套完整的用户登录授权系统,在认证服务上主要有三个依赖:spring-cloud-starter-security、spring-cloud-starter-oauth2、spring-security-jwt

        <!--spring security相关依赖-->
        <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>

  <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>1.1.1.RELEASE</version>
        </dependency>

  • 在其他需要权限访问的服务上主要包含两个依赖:
  <!--spring security相关依赖-->
        <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>

认证服务搭建

  • 这里主要做security和oauth2的相关配置
  • 主要由以下四个配置文件

image.png

  • WebSecurityConfig主要做springsecurity相关的配置
@EnableWebSecurity //开启security服务
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) //允许在方法上加的注解来配置权限
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    DaoAuthenticationProviderCustom daoAuthenticationProviderCustom;

    //使用自己定义DaoAuthenticationProviderCustom来代替框架的DaoAuthenticationProvider
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProviderCustom);
    }

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

    @Bean
    public PasswordEncoder passwordEncoder() {
        //密码为明文方式
        return NoOpPasswordEncoder.getInstance();
        //return new BCryptPasswordEncoder();
    }

    //配置安全拦截机制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/pow/**").authenticated()//访问/r开始的请求需要认证通过
                .anyRequest().permitAll()//其它请求全部放行
                .and()
                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success
        http.logout().logoutUrl("/logout");//退出地址
    }

}

  • TokenConfig在认证服务中主要作token的生成配置
@Configuration
public class TokenConfig {

    private String SIGNING_KEY = "030321liuxinyu";//和其他服务一致来解析token

    @Autowired
    TokenStore tokenStore;

//    @Bean
//    public TokenStore tokenStore() {
//        //使用内存存储令牌(普通令牌)
//        return new InMemoryTokenStore();
//    }

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }

    //令牌管理服务
    @Bean(name="authorizationServerTokenServicesCustom")
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service=new DefaultTokenServices();
        service.setSupportRefreshToken(true);//支持刷新令牌
        service.setTokenStore(tokenStore);//令牌存储策略

        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
        service.setTokenEnhancer(tokenEnhancerChain);

        service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
        return service;
    }}
  • AuthorizationServer主要是授权方面也就是spring-cloud-starter-oauth2相关的配置
@Configuration
@EnableAuthorizationServer
@Slf4j
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {


    @Resource(name = "authorizationServerTokenServicesCustom")
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    @Autowired
    private AuthenticationManager authenticationManager;

    //客户端详情服务
    @Override
    public void configure(ClientDetailsServiceConfigurer clients)
            throws Exception {

        clients.inMemory()// 使用in-memory存储
                .withClient("starBlog")// client_id
                .secret("030321liuxinyu")//客户端密钥
//                .secret(new BCryptPasswordEncoder().encode("XcWebApp"))//客户端密钥
                .resourceIds("starBlog")//资源列表

                .authorizedGrantTypes("authorization_code", "client_credentials", "implicit", "refresh_token", "password")// 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials
                .scopes("all")// 允许的授权范围
                .autoApprove(false)//false跳转到授权页面
                //客户端接收授权码的重定向地址
                .redirectUris("http://www.xuecheng-plus.com")
        ;
    }


    //令牌端点的访问配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                .authenticationManager(authenticationManager)//认证管理器
                .tokenServices(authorizationServerTokenServices)//令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

    //令牌端点的安全配置
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
                .tokenKeyAccess("permitAll()")                    //oauth/token_key是公开
                .checkTokenAccess("permitAll()")                  //oauth/check_token公开
                .allowFormAuthenticationForClients()                //表单认证(申请令牌)
        ;
    }

}
  • DaoAuthenticationProviderCustom是一个很重要的配置,通过这个修改的springsecuritu原先提供的登录方案(账号密码登录),但当前环境肯定涉及到多种登录方式,我们通过重写additionalAuthenticationChecks和setUserDetailsService方法来自定义登录方式
@Slf4j
@Component
public class DaoAuthenticationProviderCustom extends DaoAuthenticationProvider {

 @Autowired
 public void setUserDetailsService(UserDetailsService userDetailsService) {
  super.setUserDetailsService(userDetailsService);
 }


 //不再校验密码
 protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {

 }


}

  • 至此认证服务的相关配置就完成啦,我们可以通过POST localhost:8081/oauth/token?client_id=starBlog&client_secret=030321liuxinyu&grant_type=password&username={"account":"账号","authType":"登录方式","password":"密码"}来进行测试

image.png

这就是前端用户点击登录后获得的信息,然后将该token存储在本地,用户每次访问的时候都携带token即可,

  • oauth也提供了校验token的方法POST localhost:8081/oauth/check_token?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsieHVlY2hlbmctcGx1cyJdLCJ1c2VyX25hbWUiOiJ7XCJpZFwiOjE2MTY1MTYxNjUsXCJwaG9uZVwiOlwiMTM2NDg4MzcwOTNcIixcIkVtYWlsXCI6XCIyMDY0OTg5NDAzQHFxLmNvbVwiLFwiYWNjb3VudFwiOlwiMjA2NDk4OTQwM1wifSIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2NzUyNTI3MjIsImF1dGhvcml0aWVzIjpbInRlc3QiXSwianRpIjoiMDU1MjBlOTYtMTVhOS00MjAzLWExMWItNjRjZTNhNDQ5ZTM4IiwiY2xpZW50X2lkIjoic3RhckJsb2cifQ.I6wM4jZJdSA07gTnzmlP59vzqvm1hxNcNt3ejE0mR8w

image.png

资源服务搭建

  • 引入配置文件,相比于认证服务,资源服务的配置量少很多,主要有以下两个配置文件

image.png

  • TokenConfig(和认证服务的类似,但不需要token生成)
@Configuration
public class TokenConfig {

    String SIGNING_KEY = "key";


    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }
}

  • ResouceServerConfig(资源权限的相关配置)
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {


    //资源服务标识
    public static final String RESOURCE_ID = "starBlog";

    @Autowired
    TokenStore tokenStore;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_ID)//资源 id
                .tokenStore(tokenStore)
                .stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .disable()
                .authorizeRequests()
                .antMatchers( "/**").authenticated()//所有/r/**的请求必须认证通过
                .anyRequest().permitAll()
        ;
    }

}
  • 这样资源服务就搭建好了,我们访问资源

image.png

  • 在资源服务的方法上添加@PreAuthorize("hasAuthority('test')")可以指定拥有某项权限的用户才可以访问

这样一个简单的spring-cloud-starter-oauth2+securit+jwt微服务就搭建完成了