Jhipster OAuth 2.0 / OIDC 身份验证授权 header with bearer token
Jhipster OAuth 2.0 / OIDC Authentication Authorization header with bearer token
我使用 Jhipster 生成了一个带有安全选项 OAuth 2.0 / OIDC 身份验证的应用程序。我按照 http://www.jhipster.tech/security/#okta 中的说明重新配置了所述应用程序以使用 Okta 而不是 keycloak。一切都按预期工作,登录流程按预期执行。
我现在想使用 OAuth 2.0 access_tokens 从其他客户端(Postman、Wordpress)访问我的 api 资源。我从 Okta 检索了一个有效令牌,将其添加到我的 Postman 获取 localhost:8080/api/events 请求并得到 401 响应。
日志 (https://pastebin.com/raw/R3D0GHHX) 显示 spring 安全性 oauth2 似乎不是由授权持有者令牌的存在触发的。
- Jhipster 是否支持 OAuth 2.0 / OIDC 身份验证
access_token 在 Authorization bearer header or url param out of
盒子?
- 如果不能,您能建议我应该进行哪些额外的配置吗?
OAuth2Configuration.java
@Configuration
@Profile("dev")
public class OAuth2Configuration {
public static final String SAVED_LOGIN_ORIGIN_URI = OAuth2Configuration.class.getName() + "_SAVED_ORIGIN";
private final Logger log = LoggerFactory.getLogger(OAuth2Configuration.class);
@Bean
public FilterRegistrationBean saveLoginOriginFilter() {
Filter filter = new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
if (request.getRemoteUser() == null && request.getRequestURI().endsWith("/login")) {
String referrer = request.getHeader("referer");
if (!StringUtils.isBlank(referrer) &&
request.getSession().getAttribute(SAVED_LOGIN_ORIGIN_URI) == null) {
log.debug("Saving login origin URI: {}", referrer);
request.getSession().setAttribute(SAVED_LOGIN_ORIGIN_URI, referrer);
}
}
filterChain.doFilter(request, response);
}
};
FilterRegistrationBean bean = new FilterRegistrationBean(filter);
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
@Bean
public static DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() {
return new DefaultRolesPrefixPostProcessor();
}
public static class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof FilterChainProxy) {
FilterChainProxy chains = (FilterChainProxy) bean;
for (SecurityFilterChain chain : chains.getFilterChains()) {
for (Filter filter : chain.getFilters()) {
if (filter instanceof OAuth2ClientAuthenticationProcessingFilter) {
OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationProcessingFilter =
(OAuth2ClientAuthenticationProcessingFilter) filter;
oAuth2ClientAuthenticationProcessingFilter
.setAuthenticationSuccessHandler(new OAuth2AuthenticationSuccessHandler());
}
}
}
}
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public int getOrder() {
return PriorityOrdered.HIGHEST_PRECEDENCE;
}
}
}
SecurityConfiguration.java
@Configuration
@Import(SecurityProblemSupport.class)
@EnableOAuth2Sso
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CorsFilter corsFilter;
private final SecurityProblemSupport problemSupport;
public SecurityConfiguration(CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
this.corsFilter = corsFilter;
this.problemSupport = problemSupport;
}
@Bean
public AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler() {
return new AjaxLogoutSuccessHandler();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**")
.antMatchers("/h2-console/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterBefore(corsFilter, CsrfFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler())
.permitAll()
.and()
.headers()
.frameOptions()
.disable()
.and()
.authorizeRequests()
.antMatchers("/api/profile-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/websocket/tracker").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/websocket/**").permitAll()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/v2/api-docs/**").permitAll()
.antMatchers("/swagger-resources/configuration/ui").permitAll()
.antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN);
}
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
}
application.yml
security:
basic:
enabled: false
oauth2:
client:
access-token-uri: https://dev-800787.oktapreview.com/oauth2/ausb3ecnmsz8Ucjqw0h7/v1/token
user-authorization-uri: https://dev-800787.oktapreview.com/oauth2/ausb3ecnmsz8Ucjqw0h7/v1/authorize
client-id: <okta-client-id>
client-secret: <okta-client-secret>
client-authentication-scheme: form
scope: openid profile email
resource:
filter-order: 3
user-info-uri: https://dev-800787.oktapreview.com/oauth2/ausb3ecnmsz8Ucjqw0h7/v1/userinfo
token-info-uri: https://dev-800787.oktapreview.com/oauth2/ausb3ecnmsz8Ucjqw0h7/v1/introspect
prefer-token-info: false
server:
session:
cookie:
http-only: true
您需要使用 Spring Security OAuth 的 @EnableResourceServer
来实现此功能。如果您使用的是 Okta,您还可以尝试使用其 Spring Boot Starter.
Matt 的回答为我指明了正确的方向,谢谢!
这是我当前的工作配置:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class OAuth2AuthenticationConfiguration extends ResourceServerConfigurerAdapter {
@Bean
public RequestMatcher resources() {
return new RequestHeaderRequestMatcher("Authorization");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(resources())
.authorizeRequests()
.anyRequest().authenticated();
}
}
This answer 也很有帮助,谢谢。
我使用 Jhipster 生成了一个带有安全选项 OAuth 2.0 / OIDC 身份验证的应用程序。我按照 http://www.jhipster.tech/security/#okta 中的说明重新配置了所述应用程序以使用 Okta 而不是 keycloak。一切都按预期工作,登录流程按预期执行。
我现在想使用 OAuth 2.0 access_tokens 从其他客户端(Postman、Wordpress)访问我的 api 资源。我从 Okta 检索了一个有效令牌,将其添加到我的 Postman 获取 localhost:8080/api/events 请求并得到 401 响应。
日志 (https://pastebin.com/raw/R3D0GHHX) 显示 spring 安全性 oauth2 似乎不是由授权持有者令牌的存在触发的。
- Jhipster 是否支持 OAuth 2.0 / OIDC 身份验证 access_token 在 Authorization bearer header or url param out of 盒子?
- 如果不能,您能建议我应该进行哪些额外的配置吗?
OAuth2Configuration.java
@Configuration
@Profile("dev")
public class OAuth2Configuration {
public static final String SAVED_LOGIN_ORIGIN_URI = OAuth2Configuration.class.getName() + "_SAVED_ORIGIN";
private final Logger log = LoggerFactory.getLogger(OAuth2Configuration.class);
@Bean
public FilterRegistrationBean saveLoginOriginFilter() {
Filter filter = new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
if (request.getRemoteUser() == null && request.getRequestURI().endsWith("/login")) {
String referrer = request.getHeader("referer");
if (!StringUtils.isBlank(referrer) &&
request.getSession().getAttribute(SAVED_LOGIN_ORIGIN_URI) == null) {
log.debug("Saving login origin URI: {}", referrer);
request.getSession().setAttribute(SAVED_LOGIN_ORIGIN_URI, referrer);
}
}
filterChain.doFilter(request, response);
}
};
FilterRegistrationBean bean = new FilterRegistrationBean(filter);
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
@Bean
public static DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() {
return new DefaultRolesPrefixPostProcessor();
}
public static class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof FilterChainProxy) {
FilterChainProxy chains = (FilterChainProxy) bean;
for (SecurityFilterChain chain : chains.getFilterChains()) {
for (Filter filter : chain.getFilters()) {
if (filter instanceof OAuth2ClientAuthenticationProcessingFilter) {
OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationProcessingFilter =
(OAuth2ClientAuthenticationProcessingFilter) filter;
oAuth2ClientAuthenticationProcessingFilter
.setAuthenticationSuccessHandler(new OAuth2AuthenticationSuccessHandler());
}
}
}
}
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public int getOrder() {
return PriorityOrdered.HIGHEST_PRECEDENCE;
}
}
}
SecurityConfiguration.java
@Configuration
@Import(SecurityProblemSupport.class)
@EnableOAuth2Sso
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CorsFilter corsFilter;
private final SecurityProblemSupport problemSupport;
public SecurityConfiguration(CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
this.corsFilter = corsFilter;
this.problemSupport = problemSupport;
}
@Bean
public AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler() {
return new AjaxLogoutSuccessHandler();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**")
.antMatchers("/h2-console/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterBefore(corsFilter, CsrfFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler())
.permitAll()
.and()
.headers()
.frameOptions()
.disable()
.and()
.authorizeRequests()
.antMatchers("/api/profile-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/websocket/tracker").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/websocket/**").permitAll()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/v2/api-docs/**").permitAll()
.antMatchers("/swagger-resources/configuration/ui").permitAll()
.antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN);
}
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
}
application.yml
security:
basic:
enabled: false
oauth2:
client:
access-token-uri: https://dev-800787.oktapreview.com/oauth2/ausb3ecnmsz8Ucjqw0h7/v1/token
user-authorization-uri: https://dev-800787.oktapreview.com/oauth2/ausb3ecnmsz8Ucjqw0h7/v1/authorize
client-id: <okta-client-id>
client-secret: <okta-client-secret>
client-authentication-scheme: form
scope: openid profile email
resource:
filter-order: 3
user-info-uri: https://dev-800787.oktapreview.com/oauth2/ausb3ecnmsz8Ucjqw0h7/v1/userinfo
token-info-uri: https://dev-800787.oktapreview.com/oauth2/ausb3ecnmsz8Ucjqw0h7/v1/introspect
prefer-token-info: false
server:
session:
cookie:
http-only: true
您需要使用 Spring Security OAuth 的 @EnableResourceServer
来实现此功能。如果您使用的是 Okta,您还可以尝试使用其 Spring Boot Starter.
Matt 的回答为我指明了正确的方向,谢谢!
这是我当前的工作配置:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class OAuth2AuthenticationConfiguration extends ResourceServerConfigurerAdapter {
@Bean
public RequestMatcher resources() {
return new RequestHeaderRequestMatcher("Authorization");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(resources())
.authorizeRequests()
.anyRequest().authenticated();
}
}
This answer 也很有帮助,谢谢。