zoukankan      html  css  js  c++  java
  • Spring Security OAuth2 Demo —— 密码模式(Password)

    前情回顾

    前几节分享了OAuth2的流程与授权码模式和隐式授权模式两种的Demo,我们了解到授权码模式是OAuth2四种模式流程最复杂模式,复杂程度由大至小:授权码模式 > 隐式授权模式 > 密码模式 > 客户端模式

    其中密码模式的流程是:让用户填写表单提交到授权服务器,表单中包含用户的用户名、密码、客户端的id和密钥的加密串,授权服务器先解析并校验客户端信息,然后校验用户信息,完全通过返回access_token,否则默认都是401 http状态码,提示未授权无法访问

    本文目标

    编写与说明密码模式的Spring Security Oauth2的demo实现,让未了解过相关知识的读者对此授权流程有个更直观的概念

    以下分成授权服务器与资源服务器分别进行解释,只讲关键部分,详情见Github:https://github.com/hellxz/spring-security-oauth2-learn

    搭建密码模式授权服务器

    代码结构与之前两个模式相同,这里便不再进行说明

    SecurityConfig配置

    package com.github.hellxz.oauth2.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.util.StringUtils;
    
    import java.util.ArrayList;
    import java.util.Collections;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Bean
        public AuthenticationManager authenticationManager() throws Exception {
            return super.authenticationManager();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .withUser("hellxz")
                    .password(passwordEncoder().encode("xyz"))
                    .authorities(new ArrayList<>(0));
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //所有请求必须认证
            http.authorizeRequests().anyRequest().authenticated();
        }
    }
    
    

    基本的SpringSecurity的配置,开启Spring Security的Web安全功能,填了一个用户信息,所有资源必须经过授权才可以访问

    AuthorizationConfig授权服务器配置

    package com.github.hellxz.oauth2.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    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;
    
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
    
        @Autowired
        private AuthenticationManager authenticationManager;//密码模式需要注入认证管理器
    
        @Autowired
        public PasswordEncoder passwordEncoder;
    
        //配置客户端
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            //@formatter:off
            clients.inMemory()
                    .withClient("client-a")
                      .secret(passwordEncoder.encode("client-a-secret"))
                      .authorizedGrantTypes("password") //主要是这里,开始了密码模式
                      .scopes("read_scope");
            //@formatter:on
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager);//密码模式必须添加authenticationManager
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.allowFormAuthenticationForClients()
                    .checkTokenAccess("isAuthenticated()");
        }
    }
    
    

    这里开启了授权服务器的功能,与上几篇文章中不同的是注入了AuthenticationManager,以及在configure(AuthorizationServerEndpointsConfigurer endpoints)方法中设置了AuthenticationManager

    application.properties配置sever.port=8080

    搭建资源服务器

    这里的关键就是ResourceConfig,配置比较简单与其它几个模式完全一致,模式的不同主要表现在授权服务器与客户端服务器上,资源服务器只做token的校验与给予资源

    package com.github.hellxz.oauth2.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    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;
    
    @Configuration
    @EnableResourceServer
    public class ResourceConfig extends ResourceServerConfigurerAdapter {
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Primary
        @Bean
        public RemoteTokenServices remoteTokenServices() {
            final RemoteTokenServices tokenServices = new RemoteTokenServices();
            //设置授权服务器check_token端点完整地址
            tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
            //设置客户端id与secret,注意:client_secret值不能使用passwordEncoder加密!
            tokenServices.setClientId("client-a");
            tokenServices.setClientSecret("client-a-secret");
            return tokenServices;
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            //设置创建session策略
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
            //@formatter:off
            //所有请求必须授权
            http.authorizeRequests()
                    .anyRequest().authenticated();
            //@formatter:on
        }
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId("resource1").stateless(true);
        }
    }
    
    

    ResourceController主要接收一个用户名,返回一个username与email的json串

    application.properties设置server.port=8081

    准备工作到这里就差不多了,开始测试

    测试流程

    这里客户端使用postman手动发送请求进行演示

    • 访问/oauth/token端点,获取token

      http://localhost:8080/oauth/token?username=hellxz&password=xyz&scope=read_scope&grant_type=password

      请求头:

      请求体:

    • token返回值

      {
          "access_token": "4a3c351d-770d-42aa-af39-3f54b50152e9",
          "token_type": "bearer",
          "expires_in": 43199,
          "scope": "read_scope"
      }
      
    • 使用token调用资源,访问http://localhost:8081/user/hellxz001,注意使用token添加Bearer请求头

      相当于在Headers中添加 Authorization:Bearer 4a3c351d-770d-42aa-af39-3f54b50152e9

    • 资源正确返回

    尾声

    本文仅说明密码模式的精简化配置,某些部分如资源服务再访问授权服务去校验token这部分生产环境可能会换成Jwt、Redis等tokenStore实现,授权服务器中的用户信息与客户端信息生产环境应从数据库中读取,对应Spring Security的UserDetailsService实现类或用户信息的Provider等

    最近发现博客写得相对较长,一方面有相当大的重复解释代码的部分,另一方面是代码很多不关键的地方也直接全贴出来了,博客长了代码全了,读者容易失去阅读的兴趣与探索实践的欲望。代码不全会直接贴出源码地址,暂时就这样,把这几篇关于OAuth2授权模式demo的文章赶出来

    代码早就写完了,下周可能要开始加班了,先把这些已经完成的部分写出来,后续有什么新的知识点才有时间记

  • 相关阅读:
    Docker学习笔记07_网络配置
    Docker学习笔记06_部署appache+tomcat+redis+mongo+python
    Docker学习笔记05_部署nginx+php+mysql+phpmyadmin
    Docker学习笔记04_镜像管理
    Docker学习笔记03_容器的简单应用
    Docker学习笔记02_基本操作
    Docker学习笔记01_CentOS 7安装Docker
    Cisco Ironport ESA配置拒收黑名单
    CentOS 7安装Cobra
    jvm内存模型、常见参数及调优
  • 原文地址:https://www.cnblogs.com/hellxz/p/12041495.html
Copyright © 2011-2022 走看看