问题描述
在spring coud中使用spring security + oauth2 进行单点登录时。
SecurityContextHolder.getContext().getAuthentication()获取为null
问题出现的环境背景及自己尝试过哪些方法
1.spring cloud Greenwich.SR2版本
2.spring-cloud-starter-oauth2 2.1.3
3.实现UserDetailsService接口,并完成认证信息
4.能够正常拿到access_token,refresh_token等信息
5.我认为造成这个原因可能是认证没有成功,但是又能够正确返回token,对此感到疑惑。
相关代码
这是我的实现接口
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private static Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
// 数据接口
@Autowired
private ElecUserService service;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
log.info("获取用户信息");
// 获取数据库中用户信息
ElecBranchUser user = service.selectUserDetail(name);
if (user == null) {
return null;
}
// 状态为1 并且登录错误次数小于1
if (user.getUserStatus() == 2) { // 状态为2 表示注销
return new ElecBranchUser(user.getId(), user.getUserBranch(), user.getUserLoginName(), user.getPassword(), user.getAuthorities(),
true, true, true, false);
} else if (user.getUserStatus() == 3) { // 状态为3,表示锁定
return new ElecBranchUser(user.getId(), user.getUserBranch(), user.getUserLoginName(), user.getPassword(), user.getAuthorities(),
false, true, true, true);
} else if (user.getUserErrorCount() > 5) { // 错误登录次数为5,表示锁定
return new ElecBranchUser(user.getId(), user.getUserBranch(), user.getUserLoginName(), user.getPassword(), user.getAuthorities(),
false, true, true, true);
} else {
// 获取权限
List<ElecAuth> authList = service.selectAuth(user.getId());
// 将权限添加入用户实体并返回(供全局使用)
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
authList.forEach(
auth -> {
if (auth.getAuthType() == 1)
grantedAuthorities.add(new SimpleGrantedAuthority(auth.getAuthPath()));
else grantedAuthorities.add(new SimpleGrantedAuthority(auth.getAuthPath()));
}
);
ElecBranchUser b = new ElecBranchUser(user.getId(), user.getUserBranch(), user.getUserLoginName(), user.getPassword(), grantedAuthorities,
true, true, true, true);
return b;
}
}
这是WebSecurityConfigurerAdapter接口
@Configuration
@EnableWebSecurity
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
/* @Bean
public CustomAuthenticationProvider authenticationProviderBean() {
return new CustomAuthenticationProvider();
}*/
// 设置默认的加密方式
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return new UserDetailsServiceImpl();
}
// xxx 使用密码模式,重写以下授权方式,具体查看以下链接
// https://github.com/spring-projects/spring-security-oauth/issues/1328
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.authenticationProvider(authenticationProviderBean());
// 使用自定义认证与授权
auth.userDetailsService(userDetailsServiceBean())
.passwordEncoder(passwordEncoder());
}
// 不拦截oauth要开放的资源
@Override
public void configure(WebSecurity web) throws Exception {
// 将不需要拦截的路径暴露出去,否则资源服务器访问时报 403 错误
web.ignoring()
.antMatchers("/actuator/**")
.antMatchers("/oauth/authentication")
.antMatchers("/oauth/check_token")
.antMatchers("/login/user/login") // 登录接口过滤
.antMatchers("/login/user/info") // 菜单接口过滤
.antMatchers("/authClient/**") // 认证端点过滤
.antMatchers("/assets/**")
.antMatchers("/resources/**")
.antMatchers("/v2/api-docs");
}
// 配置需要token验证的资源
@Override
protected void configure(HttpSecurity http) throws Exception {
// 取消csrf
http.csrf().disable()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 允许对于网站静态资源的无授权访问
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/webjars/**",
"/swagger-resources/**",
"/*/api-docs"
).permitAll()
// 对于获取token的rest api要允许匿名访问
.antMatchers("/oauth/**").permitAll()
.antMatchers("/login/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
// 禁用缓存
http.headers().cacheControl();
}
这是AuthorizationServerConfigurerAdapter接口配置
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);
@Bean
@Primary
@ConfigurationProperties("spring.datasource.druid")
public DataSource dataSource() {
log.info("######## 创建数据源! ########");
return DruidDataSourceBuilder
.create()
.build();
}
@Bean
public TokenStore tokenStore() {
log.info("######## 设置token生产方式 ########");
return new JdbcTokenStore(dataSource());
}
@Bean
public ClientDetailsService jdbcClientDetails() {
return new JdbcClientDetailsService(dataSource());
}
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private CustomWebResponseExceptionTranslator translator;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 允许客户端访问 /oauth/check_token 检查 token
.checkTokenAccess("isAuthenticated()")
.tokenKeyAccess("permitAll()")
.allowFormAuthenticationForClients();
}
/**
* 用来配置授权(authorizatio)以及令牌(token)的访问端点和令牌服务 核心配置 在启动时就会进行配置
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
log.info("######## 配置Auth ########");
//配置token存储方式
endpoints.tokenStore(tokenStore())
//该字段设置设置refresh token是否重复使用,true:reuse;false:no reuse.
.reuseRefreshTokens(false)
.exceptionTranslator(translator)
// 刷新token时需要自定义实现类
.userDetailsService(ApplicationContextHolder.getBean(UserDetailsServiceImpl.class))
.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
log.info("######## 获取数据库中存储的客户端信息 ########");
clients.withClientDetails(jdbcClientDetails());
}
然后我通过postman测试能够获取正确返回值:
你期待的结果是什么?实际看到的错误信息又是什么?
SecurityContextHolder.getContext().getAuthentication() 能够获取到认证之后的信息
但实际上为null