美文网首页
Spring Cloud Security[微服务安全](二)授

Spring Cloud Security[微服务安全](二)授

作者: ElliotG | 来源:发表于2020-06-09 21:02 被阅读0次

    0. 概述

    根据之前的介绍,授权码模式要先获取授权码,然后再根据授权码获取token,最后根据token获取服务器端的资源。

     

    1. 项目依赖

    • 新建一个项目命名为oauth2-server

    • 添加如下依赖

    parent

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
    </properties>
    

    spring security和oauth关键依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    application.properties

    server.port=8092
    spring.application.name=oauth2-server
    

     

    2. 添加用户服务来获取授权码

    • UserService
    /**
     * Created by KG on 2019/9/30
     */
    @Service
    public class UserService implements UserDetailsService {
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        private List<User> userList;
    
        @PostConstruct
        public void initData() {
            String password = passwordEncoder.encode("123456");
            userList = new ArrayList<>();
            userList.add(new User("kg", password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin")));
            userList.add(new User("kelvin", password, AuthorityUtils.commaSeparatedStringToAuthorityList("client")));
            userList.add(new User("david", password, AuthorityUtils.commaSeparatedStringToAuthorityList("client")));
        }
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            List<User> findUserList = userList.stream().filter(user -> user.getUsername().equals(username)).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(findUserList)) {
                return findUserList.get(0);
            } else {
                throw new UsernameNotFoundException("用户名或密码错误");
            }
        }
    }
    

    User

    /**
     * Created by KG on 2019/8/29
     */
    public class User implements UserDetails {
    
        private String username;
        private String password;
        private List<GrantedAuthority> authorities;
    
        public User(String username, String password,List<GrantedAuthority> authorities) {
            this.username = username;
            this.password = password;
            this.authorities = authorities;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return authorities;
        }
    
        @Override
        public String getPassword() {
            return password;
        }
    
        @Override
        public String getUsername() {
            return username;
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            return true;
        }
    }
    

    代码解释:
    由于是演示,我们使用了一个基于内存的硬编码的用户列表。

     

    3. 配置认证服务器

    • 新建一个config包,在config包下新建类AuthorizationServerConfig继承AuthorizationServerConfigurerAdapter
      AuthorizationServerConfig
    /**
     * 认证服务器配置
     * Created by KG on 2019/9/30
     */
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Autowired
        private AuthenticationManager authenticationManager;
    
        @Autowired
        private UserService userService;
    
        /**
         * 使用用户服务来控制认证服务器的访问
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
            endpoints.authenticationManager(authenticationManager)
                    .userDetailsService(userService);
        }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient("admin")//配置client_id
                    .secret(passwordEncoder.encode("admin123456"))//配置client-secret
                    .accessTokenValiditySeconds(3600)//配置访问token的有效期
                    .refreshTokenValiditySeconds(864000)//配置刷新token的有效期
                    .redirectUris("http://www.baidu.com")//配置redirect_uri,用于授权成功后跳转
                    .scopes("all")//配置申请的权限范围
                    .authorizedGrantTypes("authorization_code", "password");//配置grant_type,表示授权类型
        }
    }
    

    代码解释:
    在endpoints的配置中,我们正好利用了之前编写的用户服务,用来控制对认证服务器的访问权限。
    在第二个configure中我们配置了client_id和client_secrect,因为要获取token,光靠授权码还是不够的。

     

    4. 安全路由配置

    SecurityConfig

    /**
     * SpringSecurity配置
     * Created by KG on 2019/10/8
     */
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.csrf()
                    .disable()
                    .authorizeRequests()
                    .antMatchers("/oauth/**", "/login/**", "/logout/**")
                    .permitAll()
                    .anyRequest()
                    .authenticated()
                    .and()
                    .formLogin()
                    .permitAll();
        }
    }
    

    代码解释:
    我们需要配置一些例外路由,例如oauth的还有登录和登出,它们都不需要在安全框架控制之内,否则就访问不了了。

     

    5. 资源服务器配置

    ResourceServerConfig

    /**
     * 资源服务器配置
     * Created by KG on 2019/9/30
     */
    @Configuration
    @EnableResourceServer
    public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .anyRequest()
                    .authenticated()
                    .and()
                    .requestMatchers()
                    .antMatchers("/user/**");//配置需要保护的资源路径
        }
    }
    

    代码解释:
    我们添加了一个需要保护的资源路由,/user/**,我们会在稍后加上这个资源的实现。

     

    6. 保护资源实现

    UserController

    /**
     * Created by KG on 2019/9/30
     */
    @RestController
    @RequestMapping("/user")
    public class UserController {
        @GetMapping("/getCurrentUser")
        public Object getCurrentUser(Authentication authentication) {
            return authentication.getPrincipal();
        }
    }
    

     

    7. 运行步骤1 - 获取认证服务器授权并获取授权码

    授权操作 回调地址中获取授权码

    授权码地址:
    https://www.baidu.com/?code=LV2Qfu&state=normal

    授权码:
    LV2Qfu

     

    8. 运行步骤2 - 通过client_id,client_secret, 授权码请求token

    • 使用授权码请求该地址获取访问令牌:http://localhost:8092/oauth/token

    • 使用Basic认证通过client_id和client_secret构造一个Authorization头信息;


      构造请求头信息
    • 在body中添加以下参数信息,通过POST请求获取访问令牌


      请求体信息

    令牌返回结果:

    {
        "access_token": "3a586c5d-a707-4ce0-8b0e-e789058f92f5",
        "token_type": "bearer",
        "expires_in": 3599,
        "scope": "all"
    }
    

    Token:
    3a586c5d-a707-4ce0-8b0e-e789058f92f5

     

    9. 运行步骤3 - 通过token访问资源

    • 通过Bearer Token的认证模式来构造一个请求头
    Bearer Token 自动生成请求头 资源返回结果

    好了,一个简易的通过授权码和认证服务器来访问资源的模式就完成了。

    相关文章

      网友评论

          本文标题:Spring Cloud Security[微服务安全](二)授

          本文链接:https://www.haomeiwen.com/subject/rgnetktx.html