一篇文章带你搞定 SpringSecurity 和 OAuth2 的结合使用

前面我们已经学习了 OAuth 具体的知识:OAuth 学习 ,所以本篇文章我们具体学下 OAuth2 和 SpringSecurity 的结合使用

本篇主要针对的是前后端分离,也就是 Oauth 中的密码式登录

一、前期准备

添加依赖:

在这里插入图片描述
添加 oauth2 依赖和 redis 依赖,由于 oauth2 依赖是在 security 基础上添加的,所以需要先添加 security

		<dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

搭建好 redis:

在这里插入图片描述
配置 application.properties:我这没有设置redis 密码,所以不需要配置

spring.redis.host=192.168.176.128
spring.redis.port=6379
# spring.redis.password=123
spring.redis.database=0

二、Config

这里为了方便将授权服务器和资源服务器放在一起进行配置

1. 首先定义授权服务器

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
    
    /**
     * authenticationManager 主要用来支持 password的认证模式:适用于前后端分离的情况
     */
    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    RedisConnectionFactory redisConnectionFactory;
    @Autowired
    UserDetailsService userDetailsService;
    @Bean
    PasswordEncoder passwordEncoder() {
    
    
        return new BCryptPasswordEncoder();
    }
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    
    
        clients.inMemory()
                //认证模式:password 模式
                .withClient("password")
                //授权模式
                .authorizedGrantTypes("password", "refresh_token")
                //token 的过期时间:1800 秒
                .accessTokenValiditySeconds(1800)
                //资源 id :rid
                .resourceIds("rid")
                .scopes("all")
                //认证需要的密码
                .secret("$2a$10$kwLIAqAupvY87OM.O25.Yu1QKEXV1imAv7jWbDaQRFUFWSnSiDEwG");
    }

    /**
     * 配置令牌的存储
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    
    
        //将令牌存储到 redis 中
        endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    /**
     * 支持登录认证
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    
    
        security.allowFormAuthenticationForClients();
    }
}

2. 资源服务器

也就是先去授权服务器上获取 token ,然后拿着 token去资源服务器中访问资源
这是提供的资源:

    @Override
    public void configure(HttpSecurity http) throws Exception {
    
    
        http.authorizeRequests().antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasRole("user")
                .anyRequest().authenticated();
    }
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    
    
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    
    
        //注意资源id 和授权服务器中的保持一致,不然拿不到资源
        //stateless:表示资源是用过令牌来认证的
        resources.resourceId("rid").stateless(true);
    }

    /**
     * 资源路径配置
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
    
    
        http.authorizeRequests().antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasRole("user")
                .anyRequest().authenticated();
    }
}

3. SecurityConfig

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    
    @Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
    
    
        return super.authenticationManager();
    }

    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
    
    
        return super.userDetailsService();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
        auth.inMemoryAuthentication()
                .withUser("yolo").password("$2a$10$kwLIAqAupvY87OM.O25.Yu1QKEXV1imAv7jWbDaQRFUFWSnSiDEwG")
                .roles("admin")
                .and()
                .withUser("nlcs")
                .password("$2a$10$kwLIAqAupvY87OM.O25.Yu1QKEXV1imAv7jWbDaQRFUFWSnSiDEwG")
                .roles("user");
    }

    /**
     * 将 oauth 请求放行,不拦截
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.antMatcher("/oauth/**")
                .authorizeRequests()
                .antMatchers("/oauth/**").permitAll()
                .and().csrf().disable();
    }
}

三、测试

1. 定义 Controller

@RestController
public class HelloController {
    
    
    @GetMapping("/admin/hello")
    public String admin() {
    
    
        return "hello admin!";
    }

    @GetMapping("/user/hello")
    public String user() {
    
    
        return "hello user";
    }

    @GetMapping("/hello")
    public String hello() {
    
    
        return "hello";
    }
}

2. Postman 测试

用户登录首先获取 token:

在这里插入图片描述

{
    
    
	//获取资源需要携带这个 token
    "access_token": "51191622-a461-470c-b310-b8cbb08387f8",
    "token_type": "bearer",
    //刷新资源需要携带这个 token
    "refresh_token": "6ad8de6c-63fb-4343-a450-3d69775f9213",
    //过期时间
    "expires_in": 1799,
    "scope": "all"
}

访问资源:

hello 接口是都可以访问

在这里插入图片描述
yolo 具有admin 权限

在这里插入图片描述
yolo 不具有 user 权限

在这里插入图片描述
如果 token 过期,需要携带 refresh_token 获取新的 token

在这里插入图片描述
此时旧的token 失效:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/nanhuaibeian/article/details/108880268