我在个人学习项目上很难。实现oauth2困难,每当我尝试获取访问令牌时,它都会返回我
2019-11-19 22:01:35.398 ERROR 4705 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause
java.lang.StackOverflowError: null
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:448) ~[spring-security-config-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:449) ~[spring-security-config-5.2.0.RELEASE.jar:5.2.0.RELEASE]
2019-11-19 22:01:35.426 WARN 4705 --- [nio-8080-exec-2] o.s.web.servlet.PageNotFound : No mapping for POST /error
我的项目使用Spring Boot v2.2.0.RELEASE和Java 1.8版本
应用程序属性
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=test
spring.datasource.password=test123
spring.jpa.hibernate.ddl-auto=update
spring.datasource.testWhileIdle = true
spring.datasource.timeBetweenEvictionRunsMillis = 3600000
spring.datasource.validationQuery = SELECT 1
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
spring.jpa.show-sql=true
AuthorizationServerConfig
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("angular")
.secret(passwordEncoder.encode("angular"))
.scopes("read", "write")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(1800)
.refreshTokenValiditySeconds(3600 * 24)
.and()
.withClient("admin")
.secret("admin")
.scopes("read")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(1800)
.refreshTokenValiditySeconds(3600 * 24);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.reuseRefreshTokens(false)
.authenticationManager(authenticationManager);
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("financas");
return accessTokenConverter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
}
ResourceServerConfig
@Configuration
@EnableWebSecurity
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/pagamento").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.csrf().disable();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.stateless(true);
}
@Bean
public static PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
SecurityConfig
@EnableWebSecurity
@EnableAuthorizationServer
@EnableResourceServer
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
return super.userDetailsService();
}
}
现在我的刷新令牌类
RefreshTokenPostProcessor
@ControllerAdvice
public class RefreshTokenPostProcessor implements ResponseBodyAdvice<OAuth2AccessToken> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return returnType.getMethod().getName().equals("postAccessToken");
}
@Override
public OAuth2AccessToken beforeBodyWrite(OAuth2AccessToken body,
MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest request, ServerHttpResponse response) {
HttpServletRequest req = ((ServletServerHttpRequest)request).getServletRequest();
HttpServletResponse resp = ((ServletServerHttpResponse)response).getServletResponse();
DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) body;
String refreshToken = body.getRefreshToken().getValue();
adicionaRefreshTokenNoCookie(refreshToken, req, resp);
removerRefreshTokenDoBody(token);
return body;
}
private void removerRefreshTokenDoBody(DefaultOAuth2AccessToken token) {
token.setRefreshToken(null);
}
private void adicionaRefreshTokenNoCookie(String refreshToken, HttpServletRequest req, HttpServletResponse resp) {
Cookie refreshTokenCookie = new Cookie("refreshToken", refreshToken);
refreshTokenCookie.setHttpOnly(true);
refreshTokenCookie.setSecure(false); //TODO: change in production
refreshTokenCookie.setPath(req.getContextPath() + "/oauth/token");
refreshTokenCookie.setMaxAge(2592000);
resp.addCookie(refreshTokenCookie);
}
}
RefreshTokenPreProcessorFilter
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RefreshTokenPreProcessorFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
if ("/oauth/token".equalsIgnoreCase(req.getRequestURI())
&& "refresh_token".equals(req.getParameter("grant_type"))
&& req.getCookies() != null) {
for (Cookie cookie : req.getCookies()) {
if (cookie.getName().equals("refreshToken")) {
String refreshToken = cookie.getValue();
req = new MyServletRequestWrapper(req, refreshToken);
}
}
}
chain.doFilter(req, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
static class MyServletRequestWrapper extends HttpServletRequestWrapper {
private String refreshToken;
public MyServletRequestWrapper(HttpServletRequest request, String refreshToken) {
super(request);
}
@Override
public Map<String, String[]> getParameterMap() {
ParameterMap<String, String[]> map = new ParameterMap<>(getRequest().getParameterMap());
map.put("refresh_token", new String[] {refreshToken});
map.setLocked(true);
return map;
}
}
}
SpringBootApplication
@SpringBootApplication
@WebAppConfiguration
public class FinancaApplication {
public static void main(String[] args) {
SpringApplication.run(FinancaApplication.class, args);
}
}
Remembering that my authentication is in the database and I am using PasswordEncoder on the database password and authentication passwords, I would like some help setting up the server and where my error is because I cannot generate an access token, the error returned is just this one in the console. thankful
答案 0 :(得分:1)
我喜欢您的代码Felipe-这是一个写得很好的API-问题是它还会尝试发行令牌,而API则不应该这样做。
我建议您逐步阅读本教程,以了解API,UI和授权服务器的角色: https://authguidance.com/2017/09/24/basicspa-overview/
完成此操作后,我认为您将能够调整您的API并解决您自己的问题-不过,请随时发回任何后续问题。
答案 1 :(得分:0)
在现实世界中,几乎所有公司都使用第三方(云)系统作为授权服务器: https://authguidance.com/2019/09/15/developer-domain-setup/
您将以这种方式更快地学习OAuth,而且它是免费的。
然后,您只需要专注于将API和UI与符合标准的端点集成在一起,如下所示: https://authguidance.com/2019/03/24/java-spring-boot-api-oauth-coding/
如果有帮助,很高兴回答后续问题。