首页 > 解决方案 > 创建自定义 OAuth 登录页面和自定义 jwt 令牌

问题描述

我使用默认的 Spring Boot 配置创建了一个 OAuth 授权服务器,其中客户端被重定向到自动生成的登录页面,userDetailsS​​ervice 查找 User 表并进行身份验证,并且在成功身份验证后服务器返回一个 jwt 令牌。现在我想定制这个并改变两件事,但我很难做到。

1)使用我自己的 login.jsp 页面而不是自动生成的登录页面,这样我就可以有一个额外的字段(例如下拉列表)并将其与用户名和密码一起用于身份验证,因为我有不同的用户表

2)我没有使用默认的UserDetailsS​​ervice,而是尝试实现自己的AuthenticationProvider,这是因为我有多个用户表,并且希望根据额外字段中的值在正确的表中搜索用户(1中提到的下拉列表) . 另外如何获取 AuthenticationProvider 中的下拉列表值?

在我的属性文件中,我设置了: spring.mvc.view.prefix: /WEB-INF/jsp/ 和 spring.mvc.view.suffix: .jsp

@Configuration
@EnableWebSecurity
public class ServerWebSecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new DefaultAuthenticationProvider());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
            .antMatchers("/login", "/oauth/authorize")
            .and()
            .authorizeRequests()
            .anyRequest().authenticated();
    }
}
@Configuration
@EnableAuthorizationServer
@Import(ServerWebSecurityConfig.class)
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    @Qualifier("dataSource")
    private DataSource dataSource;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore()).accessTokenConverter(accessTokenConverter()).authenticationManager(authenticationManager);
    }

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        clients.withClientDetails(jdbcClientDetailsService);
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("secret");
        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

    @Bean
    public PasswordEncoder userPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
public class DefaultAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        if (authentication.getName() == null || authentication.getCredentials() == null
                || authentication.getName().isEmpty() || authentication.getCredentials().toString().isEmpty()) {
            return null;
        }

        final String userName = authentication.getName();
        final String password = (String) authentication.getCredentials();
        // final String userTable = how to get this?

        // make db query in correct table based on value of userTable
        User user = null;

        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
        for (UserAuthority authority : user.getUserAuthorities()) {
            authorities.add(new CustomGrantedAuthority(authority.getAuthority().getName()));
        }

        Map<String, String> userDetails = new HashMap<>();
        userDetails.put("username", userName);

            return new UsernamePasswordAuthenticationToken(userDetails, password, authorities);

    }

    @Override
    public boolean supports(Class<?> authentication) {
        return false;
    }

}
@Controller
public class OAuthController {

    @RequestMapping("/login")
    public String login() {
        return "login";
    }
}

I am expecting that my client app is redirected to the custom login page, once login button is pressed my custom AuthenticationProvider will lookup for user in the correct table based on the extra field in the custom login page.

标签: javamysqlspring-bootjspoauth

解决方案


对于第一点,您只需为您的WebSecurityConfigurerAdapter.

添加.formLogin().loginPage("/login").permitAll()

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.requestMatchers()
        .antMatchers("/login", "/oauth/authorize")
        .and()
        .formLogin().loginPage("/login").permitAll()
        .and()
        .authorizeRequests()
        .anyRequest().authenticated();
}

推荐阅读