在我的 Spring Boot 应用程序中,我试图配置 OAuth2 隐式流.为此,我正在尝试配置自定义登录表单.
这是我的配置:
@Configuration公共类 WebMvcConfig 扩展了 WebMvcConfigurerAdapter {@覆盖public void configureDefaultServletHandling(DefaultServletHandlerConfigurer 配置器) {配置器.启用();}@覆盖public void addViewControllers(ViewControllerRegistry 注册表) {registry.addViewController("/").setViewName("index");registry.addViewController("/login").setViewName("login");}@覆盖public void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");}}@配置@启用网络安全公共类 WebSecurityConfig 扩展了 WebSecurityConfigurerAdapter {@自动连线私人 SocialAuthenticationSuccessHandler socialAuthenticationSuccessHandler;@自动连线私有 DBUserDetailsService userDetailsService;@Value("${social.postLogin.url}")私人字符串 postLoginUrl;@覆盖公共无效配置(WebSecurity web)抛出异常{//Spring Security 忽略对 CSS 或 JS 等静态资源的请求//文件.web.ignoring().antMatchers("/static/**");}@覆盖protected void configure(HttpSecurity http) 抛出异常 {//@格式化程序:关闭http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);//在 SocialAuthenticationFilter 上设置一个自定义的 successHandler最终 SpringSocialConfigurer socialConfigurer = new SpringSocialConfigurer();socialConfigurer.addObjectPostProcessor(new ObjectPostProcessor() {@覆盖public <O extends SocialAuthenticationFilter>O postProcess(O socialAuthenticationFilter){socialAuthenticationFilter.setAuthenticationSuccessHandler(socialAuthenticationSuccessHandler);socialAuthenticationFilter.setPostLoginUrl(postLoginUrl);返回 socialAuthenticationFilter;}});http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/oauth/authorize").authenticated()//任何人都可以访问网址.antMatchers("/auth/**").permitAll().antMatchers("/actuator/health").permitAll().antMatchers("/actuator/**").hasAuthority("PERMISSION_READ_ACTUATOR_DATA").antMatchers("/login").permitAll().anyRequest().authenticated().and().formLogin().loginPage("/登录").loginProcessingUrl("/登录").usernameParameter("用户名").passwordParameter("密码").permitAll()//将 SocialAuthenticationFilter 添加到 Spring Security 的过滤器链..and()//应用来自 socialConfigurer 的配置(添加 SocialAuthenticationFilter).apply(socialConfigurer);//@格式化程序:打开}/*** 配置处理认证的认证管理器bean* 请求.*/@覆盖protected void configure(AuthenticationManagerBuilder auth) 抛出异常 {auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());}@覆盖@豆公共 AuthenticationManager authenticationManagerBean() 抛出异常 {返回 super.authenticationManagerBean();}}@配置公共类 OAuth2ServerConfig {私有静态最终字符串RESOURCE_ID =restservice";@自动连线私有 DBUserDetailsService userDetailsService;@豆@主要公共 DefaultTokenServices tokenServices() {DefaultTokenServices defaultTokenServices = new DefaultTokenServices();defaultTokenServices.setTokenStore(tokenStore());defaultTokenServices.setSupportRefreshToken(true);返回 defaultTokenServices;}@豆公共 JwtAccessTokenConverter accessTokenConverter() {JwtAccessTokenConverter 转换器 = 新 JwtAccessTokenConverter() {@覆盖公共 OAuth2AccessToken 增强(OAuth2AccessToken accessToken,OAuth2Authentication 认证){DBUserDetails user = (DBUserDetails) authentication.getUserAuthentication().getPrincipal();最终映射additionalInfo = new HashMap();additionalInfo.put("user_id", user.getUser().getId());((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);OAuth2AccessToken EnhancedToken = super.enhance(accessToken, authentication);返回增强令牌;}};转换器.setSigningKey("123");DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();userTokenConverter.setUserDetailsService(userDetailsService);accessTokenConverter.setUserTokenConverter(userTokenConverter);转换器.setAccessTokenConverter(accessTokenConverter);返回转换器;}@豆公共令牌商店令牌商店(){返回新的 JwtTokenStore(accessTokenConverter());}@配置@EnableAuthorizationServer受保护的静态类 AuthorizationServerConfiguration 扩展了 AuthorizationServerConfigurerAdapter {@自动连线@Qualifier("authenticationManagerBean")私有 AuthenticationManager authenticationManager;@自动连线私有 TokenStore 令牌存储;@自动连线私人 TokenEnhancer tokenEnhancer;@覆盖public void configure(AuthorizationServerEndpointsConfigurer endpoints) 抛出异常 {//@格式化程序:关闭端点.tokenStore(tokenStore).tokenEnhancer(tokenEnhancer).authenticationManager(this.authenticationManager);//@格式化程序:打开}@覆盖公共无效配置(ClientDetailsServiceConfigurer 客户端)抛出异常 {//@格式化程序:关闭客户.inMemory().withClient("clientapp").authorizedGrantTypes("密码","refresh_token")//.authorizedGrantTypes("隐式").authorities("ROLE_CLIENT").scopes("读", "写").resourceIds(RESOURCE_ID).secret("123456").and().withClient("clientapp1").authorizedGrantTypes("隐式").scopes("读", "写").autoApprove(true);//@格式化程序:打开}}@配置@EnableResourceServer受保护的静态类 ResourceServerConfiguration 扩展了 ResourceServerConfigurerAdapter {@自动连线私有 ResourceServerTokenServices 令牌服务;@覆盖公共无效配置(ResourceServerSecurityConfigurer 资源){//@格式化程序:关闭资源.resourceId(RESOURCE_ID).tokenServices(tokenService);//@格式化程序:打开}@覆盖公共无效配置(HttpSecurity http)抛出异常{//@格式化程序:关闭http.authorizeRequests().antMatchers("/profile/*").authenticated().and().csrf().disable().sessionManagement().sessionCreationPolicy(STATELESS);//@格式化程序:打开}}}login.html 页面 Thymeleaf 模板:
<html xmlns="www.w3/1999/xhtml" xmlns:th="www.thymeleaf"xmlns:sec="www.thymeleaf/thymeleaf-extras-springsecurity3"><头><title>Spring 安全示例 </title><身体><div th:if="${param.error}">用户名和密码无效.<div th:if="${param.logout}">您已注销.
<form th:action="@{/login}" method="post"><div><标签>用户名:<input type="text" name="username"/></label></div><div><标签>密码:<input type="password" name="password"/></label></div><div><input type="submit" value="Sign In"/></div></表单></html>
Maven 工件版本:
1.4.0.RELEASE<spring-security-core.version>4.1.3.RELEASE</spring-security-core.version><spring-security-oauth2.version>2.0.11.RELEASE</spring-security-oauth2.version>现在,当我尝试访问以下网址时:
localhost:8080/oauth/authorize?response_type=implicit&client_id=clientapp1我已成功重定向到位于 localhost:8080/login 的登录页面,但是当我输入用户名/密码并按登录"按钮时,出现以下错误:
白标错误页面此应用程序没有明确的/error 映射,因此您将其视为后备.2016 年 9 月 24 日星期六 21:19:44 EEST出现意外错误(type=Method Not Allowed,status=405).不支持请求方法POST"我做错了什么以及如何解决这个问题?
更新
在调试中我可以看到以下输出:
DispatcherServlet 名为dispatcherServlet",处理 [/login] 的 POST 请求2016-09-25 10:04:43 [http-nio-8080-exec-2] 调试 o.s.w.s.m.m.a.RequestMappingHandlerMapping -查找路径/login 的处理程序方法2016-09-25 10:04:43 [http-nio-8080-exec-2] 调试 o.s.w.s.m.m.a.RequestMappingHandlerMapping -没有找到 [/login] 的处理程序方法2016-09-25 10:04:43 [http-nio-8080-exec-2] 调试 o.s.w.s.h.SimpleUrlHandlerMapping -使用处理程序 [org.springframework.web.servlet.mvc.ParameterizableViewController@c85e70] 和 1 个拦截器将 [/login] 映射到 HandlerExecutionChain2016-09-25 10:04:43 [http-nio-8080-exec-2] 调试 o.s.w.s.m.a.ResponseStatusExceptionResolver -解决处理程序异常 [org.springframework.web.servlet.mvc.ParameterizableViewController@c85e70]:org.springframework.web.HttpRequestMethodNotSupportedException:不支持请求方法POST"2016-09-25 10:04:43 [http-nio-8080-exec-2] 调试 o.s.w.s.m.s.DefaultHandlerExceptionResolver -解决处理程序异常 [org.springframework.web.servlet.mvc.ParameterizableViewController@c85e70]:org.springframework.web.HttpRequestMethodNotSupportedException:不支持请求方法POST"2016-09-25 10:04:43 [http-nio-8080-exec-2] 警告 o.s.web.servlet.PageNotFound -不支持请求方法POST"2016-09-25 10:04:43 [http-nio-8080-exec-2] 调试 o.s.s.w.h.writers.HstsHeaderWriter -不注入 HSTS 标头,因为它与 requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@16580a4 不匹配2016-09-25 10:04:43 [http-nio-8080-exec-2] 调试 os.web.servlet.DispatcherServlet -Null ModelAndView 返回给 DispatcherServlet,名称为dispatcherServlet":假设 HandlerAdapter 已完成请求处理2016-09-25 10:04:43 [http-nio-8080-exec-2] 调试 os.web.servlet.DispatcherServlet -成功完成请求另外,关于这个问题还有另一个问题没有提供答案使用 oauth2 和 formlogin 未在 Spring Security 中调用 usernamepasswordauthenticationfilter
解决方案这是我的 ResourceServer 配置错误的问题.
使用以下配置,一切正常:
@Configuration@EnableResourceServer受保护的静态类 ResourceServerConfiguration 扩展了 ResourceServerConfigurerAdapter {@自动连线私有 ResourceServerTokenServices 令牌服务;@覆盖公共无效配置(ResourceServerSecurityConfigurer 资源){//@格式化程序:关闭资源.resourceId(RESOURCE_ID).tokenServices(tokenService);//@格式化程序:打开}@覆盖公共无效配置(HttpSecurity http)抛出异常{//@格式化程序:关闭http.antMatcher("/api/**" ).authorizeRequests().anyRequest().authenticated().and().csrf().disable().sessionManagement().sessionCreationPolicy(STATELESS);//@格式化程序:打开}}In my Spring Boot application I'm trying to configure OAuth2 implicit flow. For this purpose I'm trying to configure custom login form.
This is my config:
@Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/login").setViewName("login"); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); } } @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SocialAuthenticationSuccessHandler socialAuthenticationSuccessHandler; @Autowired private DBUserDetailsService userDetailsService; @Value("${social.postLogin.url}") private String postLoginUrl; @Override public void configure(WebSecurity web) throws Exception { // Spring Security ignores request to static resources such as CSS or JS // files. web.ignoring().antMatchers("/static/**"); } @Override protected void configure(HttpSecurity http) throws Exception { // @formatter:off http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class); // Set a custom successHandler on the SocialAuthenticationFilter final SpringSocialConfigurer socialConfigurer = new SpringSocialConfigurer(); socialConfigurer.addObjectPostProcessor(new ObjectPostProcessor<SocialAuthenticationFilter>() { @Override public <O extends SocialAuthenticationFilter> O postProcess(O socialAuthenticationFilter) { socialAuthenticationFilter.setAuthenticationSuccessHandler(socialAuthenticationSuccessHandler); socialAuthenticationFilter.setPostLoginUrl(postLoginUrl); return socialAuthenticationFilter; } }); http .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/oauth/authorize").authenticated() //Anyone can access the urls .antMatchers("/auth/**").permitAll() .antMatchers("/actuator/health").permitAll() .antMatchers("/actuator/**").hasAuthority("PERMISSION_READ_ACTUATOR_DATA") .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .loginProcessingUrl("/login") .usernameParameter("username") .passwordParameter("password") .permitAll() //Adds the SocialAuthenticationFilter to Spring Security's filter chain. .and() // apply the configuration from the socialConfigurer (adds the SocialAuthenticationFilter) .apply(socialConfigurer); // @formatter:on } /** * Configures the authentication manager bean which processes authentication * requests. */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder()); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } } @Configuration public class OAuth2ServerConfig { private static final String RESOURCE_ID = "restservice"; @Autowired private DBUserDetailsService userDetailsService; @Bean @Primary public DefaultTokenServices tokenServices() { DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); defaultTokenServices.setTokenStore(tokenStore()); defaultTokenServices.setSupportRefreshToken(true); return defaultTokenServices; } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter() { @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { DBUserDetails user = (DBUserDetails) authentication.getUserAuthentication().getPrincipal(); final Map<String, Object> additionalInfo = new HashMap<>(); additionalInfo.put("user_id", user.getUser().getId()); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication); return enhancedToken; } }; converter.setSigningKey("123"); DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter(); userTokenConverter.setUserDetailsService(userDetailsService); accessTokenConverter.setUserTokenConverter(userTokenConverter); converter.setAccessTokenConverter(accessTokenConverter); return converter; } @Bean public TokenStore tokenStore() { return new JwtTokenStore(accessTokenConverter()); } @Configuration @EnableAuthorizationServer protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager; @Autowired private TokenStore tokenStore; @Autowired private TokenEnhancer tokenEnhancer; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { // @formatter:off endpoints .tokenStore(tokenStore) .tokenEnhancer(tokenEnhancer) .authenticationManager(this.authenticationManager); // @formatter:on } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // @formatter:off clients .inMemory() .withClient("clientapp") .authorizedGrantTypes("password","refresh_token") //.authorizedGrantTypes("implicit") .authorities("ROLE_CLIENT") .scopes("read", "write") .resourceIds(RESOURCE_ID) .secret("123456") .and() .withClient("clientapp1") .authorizedGrantTypes("implicit") .scopes("read", "write") .autoApprove(true); // @formatter:on } } @Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Autowired private ResourceServerTokenServices tokenService; @Override public void configure(ResourceServerSecurityConfigurer resources) { // @formatter:off resources .resourceId(RESOURCE_ID) .tokenServices(tokenService); // @formatter:on } @Override public void configure(HttpSecurity http) throws Exception { // @formatter:off http .authorizeRequests() .antMatchers("/profile/*").authenticated() .and().csrf() .disable().sessionManagement().sessionCreationPolicy(STATELESS); // @formatter:on } } }login.html page Thymeleaf template:
<!DOCTYPE html> <html xmlns="www.w3/1999/xhtml" xmlns:th="www.thymeleaf" xmlns:sec="www.thymeleaf/thymeleaf-extras-springsecurity3"> <head> <title>Spring Security Example </title> </head> <body> <div th:if="${param.error}"> Invalid username and password. </div> <div th:if="${param.logout}"> You have been logged out. </div> <form th:action="@{/login}" method="post"> <div><label> User Name : <input type="text" name="username"/> </label></div> <div><label> Password: <input type="password" name="password"/> </label></div> <div><input type="submit" value="Sign In"/></div> </form> </body> </html>Maven artifact versions:
<spring.boot.version>1.4.0.RELEASE</spring.boot.version> <spring-security-core.version>4.1.3.RELEASE</spring-security-core.version> <spring-security-oauth2.version>2.0.11.RELEASE</spring-security-oauth2.version>Right now, when I'm trying to access following url:
localhost:8080/oauth/authorize?response_type=implicit&client_id=clientapp1I'm successfully redirected to my login page at localhost:8080/login but when I enter username/password and press "Sign in" button I'm getting following error:
Whitelabel Error Page This application has no explicit mapping for /error, so you are seeing this as a fallback. Sat Sep 24 21:19:44 EEST 2016 There was an unexpected error (type=Method Not Allowed, status=405). Request method 'POST' not supportedWhat am I doing wrong and how to fix this issue ?
UPDATED
In debug I can see the following output:
DispatcherServlet with name 'dispatcherServlet' processing POST request for [/login] 2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /login 2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Did not find handler method for [/login] 2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.w.s.h.SimpleUrlHandlerMapping - Mapping [/login] to HandlerExecutionChain with handler [org.springframework.web.servlet.mvc.ParameterizableViewController@c85e70] and 1 interceptor 2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver - Resolving exception from handler [org.springframework.web.servlet.mvc.ParameterizableViewController@c85e70]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported 2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolving exception from handler [org.springframework.web.servlet.mvc.ParameterizableViewController@c85e70]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported 2016-09-25 10:04:43 [http-nio-8080-exec-2] WARN o.s.web.servlet.PageNotFound - Request method 'POST' not supported 2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.s.w.h.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@16580a4 2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling 2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed requestAlso, there is another question about this issue with no answer provided usernamepasswordauthenticationfilter not getting invoked in spring security with oauth2 and formlogin
解决方案It was my issue with a wrong configuration of ResourceServer.
With a following configuration everything is working fine:
@Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Autowired private ResourceServerTokenServices tokenService; @Override public void configure(ResourceServerSecurityConfigurer resources) { // @formatter:off resources .resourceId(RESOURCE_ID) .tokenServices(tokenService); // @formatter:on } @Override public void configure(HttpSecurity http) throws Exception { // @formatter:off http .antMatcher("/api/**" ) .authorizeRequests().anyRequest().authenticated() .and() .csrf().disable() .sessionManagement().sessionCreationPolicy(STATELESS); // @formatter:on } }
更多推荐
Spring Boot OAuth2 隐式流 + 表单登录和请求方法“POST"不支持错误
发布评论