Spring Security OAuth2密码模式下无法触发loadUserByUsername方法
OAuth2密码模式下自定义UserDetailsService的loadUserByUsername方法未触发问题
我在配置Spring Security OAuth2授权服务器时遇到问题:已经在AuthorizationServerConfig中配置了自定义UserDetailsService(userServiceDetailsServiceImpl),但使用OAuth2密码模式认证时,始终无法触发loadUserByUsername方法。
我通过Basic Authentication访问/oauth/token端点,确认用户名和密码正确,但就是进不了UserServiceDetailsServiceImpl的loadUserByUsername方法。

相关代码片段
AuthorizationServerConfig
@EnableAuthorizationServer // 开启授权服务器 @Configuration public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Qualifier("userServiceDetailsServiceImpl") @Autowired private UserDetailsService userDetailsService; /** * 配置验证管理器,UserdetailService */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) .tokenStore(jwtTokenStore()) // jwt 来存储我们的token .tokenEnhancer(jwtAccessTokenConverter()); // jwt还需要设置决定token的产生方式 super.configure(endpoints); } private TokenStore jwtTokenStore() { JwtTokenStore jwtTokenStore = new JwtTokenStore(jwtAccessTokenConverter()); return jwtTokenStore; } public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); // 加载我们的私钥 ClassPathResource classPathResource = new ClassPathResource("coinexchange.jks"); KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource, "coinexchange".toCharArray()); jwtAccessTokenConverter.setKeyPair( keyStoreKeyFactory.getKeyPair("coinexchange", "coinexchange".toCharArray())); return jwtAccessTokenConverter; } }
ResourceServerConfig
@EnableResourceServer @Configuration public class ResourceServerConfig extends ResourceServerConfigurerAdapter {}
WebSecurityConfig
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); http.authorizeRequests().anyRequest().authenticated(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /** * 密码加密 * @return */ @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } }
UserServiceDetailsServiceImpl
@Service public class UserServiceDetailsServiceImpl implements UserDetailsService { @Autowired private JdbcTemplate jdbcTemplate; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); String loginType = requestAttributes.getRequest().getParameter("login_type"); // 区分后台人员还是用户 if(StringUtils.isEmpty(loginType)) { throw new AuthenticationServiceException("登陆类型不能为null"); } UserDetails userDetails = null; try { switch (loginType) { case LoginConstant.ADMIN_TYPE: userDetails = loadSysUserByUsername(s); // 后台人员的登录 break; case LoginConstant.MEMBER_TYPE: userDetails = loadMemberUserByUsername(s); // 普通会员登录 break; default: throw new AuthenticationServiceException("暂不支持的登陆方式:" + loginType); } }catch (IncorrectResultSizeDataAccessException e) { // 用户不存在 throw new UsernameNotFoundException("用户名" + s + "不存在"); } return userDetails; } /** * 后台管理员登录 * @param username * @return */ private UserDetails loadSysUserByUsername(String username) { // 1. 使用用户名查询用户 return jdbcTemplate.queryForObject(LoginConstant.QUERY_ADMIN_SQL, new RowMapper<User>() { @Override public User mapRow(ResultSet resultSet, int rowNum) throws SQLException { if(resultSet.wasNull()) { throw new UsernameNotFoundException("用户名" + username + "不存在"); } long id = resultSet.getLong("id"); // 用户id String password = resultSet.getString("password"); // 用户密码 int status = resultSet.getInt("status"); // 用户状态 return new User( // 3. 封装成一个USerDetails对象,返回 String.valueOf(id), // 使用id->username password, status == 1, true, true, true, getSysUserPermissions(id) // 2. 查询该用户的权限 ); } }, username); } /** * 通过用户id查询用户的权限 * @param id * @return */ private Collection<? extends GrantedAuthority> getSysUserPermissions(long id) { // 1. 当用户为超级管理员,他拥有所有的权限 String roleCode = jdbcTemplate.queryForObject(LoginConstant.QUERY_ROLE_CODE_SQL, String.class); List<String> permissions = null; // 权限的名称 if(LoginConstant.ADMIN_ROLE_CODE.equals(roleCode)) { // 超级管理员 permissions = jdbcTemplate.queryForList(LoginConstant.QUERY_ALL_PERMISSIONS, String.class); } else { // 2. 普通用户,需要使用角色->权限 permissions = jdbcTemplate.queryForList(LoginConstant.QUERY_PERMISSION_SQL, String.class, id); } if(permissions == null || permissions.isEmpty()) { return Collections.emptyList(); } return permissions.stream() .distinct() // 去重 .map(perm->new SimpleGrantedAuthority(perm)) .collect(Collectors.toSet()); } }
application.yml
server: port: 9999 spring: application: name: authorization-server cloud: nacos: discovery: server-addr: nacos-server:8848 datasource: url: jdbc:mysql://127.0.0.1:3306/coin-exchange?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8 username: root driver-class-name: com.mysql.cj.jdbc.Driver
预期效果
希望在OAuth2密码模式认证过程中,能够进入UserServiceDetailsServiceImpl的loadUserByUsername方法,完成用户身份验证。
已尝试的解决方案
- 在AuthorizationServerConfig中正确注入UserDetailsService,并在AuthorizationServerEndpointsConfigurer中完成配置。
- 检查Basic Authentication的配置和请求头,确保用户名和密码输入正确。
- 查看日志,未发现loadUserByUsername方法被调用的记录。
求助问题
- 是否有人遇到过类似问题?是如何解决的?
- 我应该检查哪些方面来确定loadUserByUsername方法未被触发的原因?
- 还有哪些因素可能会影响OAuth2密码模式的认证流程?
非常感谢您的帮助和建议!
内容的提问来源于stack exchange,提问作者Caseythekiwi
相关产品推荐
相关产品推荐

