【问题标题】:Oauth 2: Spring Boot 2: Auth Server /oauth/check_token returns user_name as nullOauth 2:Spring Boot 2:Auth Server /oauth/check_token 将 user_name 返回为 null
【发布时间】:2019-12-01 00:08:52
【问题描述】:

我已经实现了 2 个不同的服务器:auth 服务器和一个使用 Spring Boot 2.1.6.RELEASE 和 spring-cloud-starter-oauth2 版本 Greenwich.RELEASE 的资源服务器

我能够成功从身份验证服务器获取 access_token,用它来访问资源服务器上受保护的 api。

但是,我无法在身份验证服务器的 /oauth/check_token 端点返回的响应中获取 user_name,但我可以确认 user_name 存在于用户表中。

curl http://localhost:5000/oauth/check_token?token=a3ee84ee-6d3a-4a8f-af19-5446b55c637f | jq . 

返回以下内容:

  {
    "aud": [
      "article"
    ],
    "user_name": null,
    "scope": [
      "READ",
      "WRITE",
      "UPDATE",
      "DELETE"
    ],
    "active": true,
    "exp": 1563849438,
    "authorities": [
      "ROLE_administrator",
      "create_article",
      "read_article",
      "delete_article",
      "update_article"
    ],
    "client_id": "myclient"
  }

授权服务器配置

@Configuration
public class AuthorizationServerConfiguration implements AuthorizationServerConfigurer {

    private PasswordEncoder passwordEncoder;
    private DataSource dataSource;
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    public AuthorizationServerConfiguration(
            PasswordEncoder passwordEncoder,
            DataSource dataSource,
            AuthenticationManager authenticationManager) {
        this.passwordEncoder = passwordEncoder;
        this.dataSource = dataSource;
        this.authenticationManager = authenticationManager;
    }

    @Bean
    TokenStore jdbcTokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        return new JwtAccessTokenConverter();
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
      //  security.checkTokenAccess("isAuthenticated()").tokenKeyAccess("permitAll()");
        security.checkTokenAccess("permitAll()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.tokenStore(jdbcTokenStore());
        endpoints.authenticationManager(authenticationManager);
        //TODO JWT
        // endpoints.accessTokenConverter(accessTokenConverter());
    }
}

网络安全配置

@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

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

    @Bean
    PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/version").permitAll()
                .antMatchers("/api/**").authenticated();
    }
}

UserDetailsS​​erviceImpl

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
    private UserRepository userRepository;

    @Autowired
    public UserDetailsServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        Optional<User> optionalUser = userRepository.findByUserName(userName);
        optionalUser.orElseThrow(() -> new UsernameNotFoundException("Username or password wrong"));

        UserDetails userDetails = new AuthUserDetail(optionalUser.get());
        new AccountStatusUserDetailsChecker().check(userDetails);
        return userDetails;
    }
}

主应用类

@SpringBootApplication
@EnableAuthorizationServer
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

}

AuthUserDetail

package com.myapplication.models;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class AuthUserDetail extends User implements UserDetails {

    public AuthUserDetail(User user) {
        super(user);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();

        getRoles().forEach(role -> {
            grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
            role.getPermissions().forEach(permission -> {
                grantedAuthorities.add(new SimpleGrantedAuthority(permission.getName()));
            });

        });
        return grantedAuthorities;
    }

    @Override
    public String getPassword() {
        return super.getPassword();
    }

    @Override
    public String getUsername() {
        return super.getUserName();
    }

    @Override
    public boolean isAccountNonExpired() {
        return super.isAccountNonExpired();
    }

    @Override
    public boolean isAccountNonLocked() {
        return super.isAccountNonLocked();
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return super.isCredentialsNonExpired();
    }

    @Override
    public boolean isEnabled() {
        return super.isEnabled();
    }
}

用户

package com.myapplication.models;

import lombok.Data;

import javax.persistence.*;
import java.util.List;

@Entity
@Table(name = "user")
@Data
public class User {
    public User() {
    }

    public User(User user) {
        this.userName = user.getUserName();
        this.password = user.getPassword();
        this.email = user.getEmail();
        this.enabled = user.isEnabled();
        this.accountNonExpired = user.isAccountNonExpired();
        this.credentialsNonExpired = user.isCredentialsNonExpired();
        this.accountNonLocked = user.isAccountNonLocked();
        this.roles = user.getRoles();
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(name = "user_name")
    private String userName;
    @Column(name = "password")
    private String password;
    @Column(name = "email")
    private String email;
    @Column(name = "enabled")
    private boolean enabled;
    @Column(name = "account_non_expired")
    private boolean accountNonExpired;
    @Column(name = "credentials_non_expired")
    private boolean credentialsNonExpired;
    @Column(name = "account_non_locked")
    private boolean accountNonLocked;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "role_user", joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
            inverseJoinColumns = {
                    @JoinColumn(name = "role_id", referencedColumnName = "id")})
    private List<Role> roles;
}

用户表 ddl

create table if not exists user
(
    id int auto_increment
        primary key,
    user_name varchar(100) not null,
    password varchar(1024) not null,
    email varchar(1024) not null,
    enabled tinyint not null,
    account_non_expired tinyint not null,
    credentials_non_expired tinyint not null,
    account_non_locked tinyint not null,
    constraint user_name
        unique (user_name)
);

【问题讨论】:

  • @dur 既然你问了这个问题,我想知道它是否与 DefaultUserAuthenticationConverter 或 DefaultAccessTokenConverter 有关 - 也许我需要提供自己的实现来丰富用户对象?
  • @dur 这是我自己的包 - com.myapplication.models 包是用户类所在的位置。此外 AuthUserDetail 也在同一个包中 - com.myapplication.models
  • 发生这种情况是因为我更改了用户表 - 列名(包含下划线)与预期的默认用户表不同吗?
  • 能否在return userDetails 处添加断点并检查是否设置了用户名?
  • @dur 当我运行 curl -u myclient:secret -X POST localhost:5000/oauth/check_token\?token=a8e194a7-cd0d-4e02-8abf-8077a689d618 | jq . 时,我没有在 UserDetailsS​​erviceImpl 中的那一行打断点

标签: spring-boot spring-security oauth-2.0


【解决方案1】:

在将用户表的列名从 camelCase 修改为 underscore_case 后,我遇到了同样的问题。

为了解决这个问题,我确保用户、权限和角色类正在实现 Serializable 类

@Entity
@Table(name = "user")
public class User implements Serializable {
----
----
}

如果添加了以下 spring jpa 属性,则还删除

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

【讨论】:

  • 这对我有用。谢谢。但我没有删除spring.jpa.hibernate.naming.physical-strategy 属性
  • 谢谢你帮我节省了我花了一天的时间。再次感谢。
  • 我正在使用 mongodb,因为它也有效
猜你喜欢
  • 2019-02-18
  • 1970-01-01
  • 1970-01-01
  • 2017-06-29
  • 2020-06-30
  • 1970-01-01
  • 2015-12-25
  • 2019-11-10
  • 2017-08-31
相关资源
最近更新 更多