spring security oauth2 2.1.3

新手上路,请多包涵

问题描述

在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测试能够获取正确返回值:

clipboard.png

你期待的结果是什么?实际看到的错误信息又是什么?

SecurityContextHolder.getContext().getAuthentication() 能够获取到认证之后的信息
但实际上为null

阅读 1.5k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题