SpringBoot2 配置 Oauth2 置顶!

  |   0 评论   |   0 浏览

前言

自己用Springboot2 配置 Oauth2 的时候,参照了网上的很多例子,不过大部分例子都特别繁琐,而且有些地方写的不是很详细,坑太多,非常不适合第一次配置 Oauth2 的。所以我准备写一个最精简的配置 Oauth2 的文章。
Oauth2 的简介这里就不写了,不懂的可以直接百度。本文章注重于Oauth2的基本配置,不注重原理。

依赖

        <!-- SpringSecurity  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        
        <!-- jpa  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>


        <!-- 由于一些注解和API从spring security5.0中移除,所以需要导入下面的依赖包  -->
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>


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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

这里持久层框架用的 JPA ,这个不影响,不喜欢的可以自己换。

数据源的配置

server.port=80

spring.datasource.url=jdbc:mysql://192.168.1.250:3306/spring_oauth2?useSSL=false&serverTimezone=Hongkong
spring.datasource.username: root
spring.datasource.password: 123456
spring.datasource.driver-class-name: com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

spring.main.allow-bean-definition-overriding=true

配置 WebSecurity

@Configuration        //配置类注解
@EnableWebSecurity    //开启WebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)  //开启方法级的注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    //引入UserServiceDetail
    @Autowired
    UserServiceDetail userServiceDetail;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //  允许所有人访问 '/oauth' 以下的目录  
        http.authorizeRequests()
             .antMatchers("/oauth/**").permitAll()
             .anyRequest().authenticated();

        http.csrf().disable()      //关闭  csrf 
                  .and()
                  .authorizeRequests()
                  .antMatchers("/**").authenticated()    //其他目录需要认证
                  .and()
                  .httpBasic();                          //开启基本http验证
    }
    
    // 把 PasswordEncoder 放到  Spring 容器中
    // Springboot2 貌似必须把这个配置到 Spring 容器中,不然会报错
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userServiceDetail).passwordEncoder(passwordEncoder());
    }

    
    //把 AuthenticationManager 配置到 Spring 容器中,配置Oauth2 的时候会用到
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

实体类

User 类

 @Entity
public class User implements UserDetails {


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false,unique = true)
    private String username;

    @Column
    private String password;

    @ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    @JoinTable(name="user_role",joinColumns = @JoinColumn(name = "user_id",referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name="role_id",referencedColumnName = "id")
    )
    private List<Role> 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;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setAuthorities(List<Role> authorities) {
        this.authorities = authorities;
    }
}

角色类

@Entity
public class Role implements GrantedAuthority {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Override
    public String getAuthority() {
        return name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }
}

UserDao

public interface UserDao extends JpaRepository<User,Long> {
    User findByUsername(String username);
}

UserServiceDetail

  • UserServiceDetail 需要实现org.springframework.security.core.userdetails.UserDetailsService 包下的 UserDetailsService 接口。
  • 重写 loadUserByUsername 方法。该方法应该返回用户的基本信息,包括权限权限信息,即 UserDetails 对象。
@Service
public class UserServiceDetail implements UserDetailsService {

    @Autowired
    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        UserDetails userDetails = userDao.findByUsername(username);

        return userDetails;
    }
}

配置 Oauth2

@Configuration
@EnableAuthorizationServer
    public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
        
        //引入 SpringSecurity 中配置的 AuthenticationManager
        @Autowired
        private AuthenticationManager authenticationManager;
        
        //引入 UserServiceDetail 服务
        @Autowired
        private UserServiceDetail userServiceDetail;
        
        //配置客户端信息
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            //这里直接把配置信息保存在内存中
            clients.inMemory()
                    .withClient("service-hi")
                    //这里必须使用加密
                    .secret(new BCryptPasswordEncoder().encode("123456"))
                    //配置 GrantTypes
                    //支持 刷新token
                    // 使用密码模式                     
                    .authorizedGrantTypes("client_credentials","refresh_token","password")
                    //这个随便配了一个,暂时没用到
                    .scopes("server");
        }
        
        //配置 Token 的节点 和 Token 服务
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

            endpoints.tokenStore(tokenStore())
                    .authenticationManager(authenticationManager)
                    .userDetailsService(userServiceDetail)
                    .accessTokenConverter(jwtTokenEnhancer());
        }
       
        // 配置 Token 节点的安全策略
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.tokenKeyAccess("permitAll()")
                    .checkTokenAccess("isAuthenticated()");
        }
    
    //使用 jwt 
    @Bean
    public TokenStore tokenStore(){
        return new JwtTokenStore(jwtTokenEnhancer());
    }
    
    // 配置 jwt 生成 策略
    @Bean
    public JwtAccessTokenConverter jwtTokenEnhancer(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123456");   //密钥
        return converter;
    }
}

如果 token 使用 内存形式的话,更简单
直接 把上面的 tokenStore方法改为

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

并删除 jwtTokenEnhancer 方法。

最终测试

测试这里,我要写的详细一些,之前踩过很多坑。

启动项目以后,在日志中可以看到下面三个地址
[Ant [pattern='/oauth/token'],
Ant [pattern='/oauth/token_key'],
Ant [pattern='/oauth/check_token']]

这里分别说一下,并进行测试。

  • /oauth/token: 这个是获取 以及 刷新 token 的地址
  • /oauth/token_key: 这个我自己测试了一下,应该是返回 客户端密码以及加密方式(可能不对,欢迎大佬指正)
  • /oauth/check_token:这个是验证 token 是否正确的地址,如果正确则返回用户基本信息

获取token

请求方式

  • 请求地址:localhost/oauth/token

  • 请求方法: POST

  • 请求参数:
    1. grant_type,我们使用的是密码模式,所以这里设置成 password
    2. username, 用户名
    3. password,密码

  • 请求头:

    请求头中,需要传入一个Authorization 参数。

    它的值为 Oauth2 配置中的客户端的用户名与密码以 '用户名:密码'的形式组合,然后再进行 Base64 编码得到的。

    本例中 客户端的用户名为 server-hi,密码为 123456,将它们组合为 server-hi:123456,进行 Base64 编码,得到 "c2VydmljZS1oaToxMjM0NTY="。

    访问的时候请求头中的参数名为 'Authorization' ,参数值为 'Basic c2VydmljZS1oaToxMjM0NTY=',注意参数值中 Basic 与 Base64编码后的字符串中间有空格

请求测试

使用postman ,按上面的方式进行测试。
在这里插入图片描述
在请求头中传入 Authorization 参数
在这里插入图片描述
返回的结果
在这里插入图片描述

验证token

请求方式

  • 请求地址: localhost/oauth/check_token
  • 请求方法:GET
  • 请求参数:
    token,传入之前获取的token
  • 请求头中也需要添加上面的验证(参照获取token)

测试

传入 token 参数
在这里插入图片描述
请求头中传入 Authorization 参数
在这里插入图片描述
返回的结果
在这里插入图片描述

访问受保护的资源

请求方式

有两种方式

  1. 在请求参数中传入 access_token 参数。值为 之前获取到的token

  2. 在请求头中传入 Authorization参数。值为 bearer + 空格 + 之前获取的token

    其中的bearer ,是获取token 时,返回的 token_type 的值。

请求地址

有两种方式访问被保护的资源

1.传入 access_token 参数
在这里插入图片描述
本例测试 返回 该当前用户的用户名.

  1. 在 请求头中传入 Authorization

在这里插入图片描述
返回结果与第一种一样

刷新 token

请求方式

  • 请求地址:localhost/oauth/token

  • 请求方法: POST

  • 请求参数:
    1. grant_type, 值为 refresh_token
    2. refresh_token, 值为 获取token 时,返回的refresh_token的值

  • 请求头:请求头中也需要添加上面的验证(参照获取token)

测试

分别传入 grant_type ,以及 refresh_token
在这里插入图片描述
在请求头中传入 Authorization
在这里插入图片描述

总结

其实 SpringBoot 配置 Oauth2 不是特别难,之前出了问题,我一直以为是我自己配错了,最后才发现请求的方式不对,所以这里在写测试的时候,写得比较详细。


标题:SpringBoot2 配置 Oauth2
作者:zh847707713
地址:http://lovehao.cn/articles/2019/06/21/1561082004876.html