【发布时间】: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();
}
}
UserDetailsServiceImpl
@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 .时,我没有在 UserDetailsServiceImpl 中的那一行打断点
标签: spring-boot spring-security oauth-2.0