我正试图在SpringMVC应用程序中实现jwt令牌身份验证。在此更改之前,身份验证仅使用basic auth进行。现在我想把基本auth和jwt结合起来。这就是迄今为止所做的事情。
@Configuration
@Order(200)
@Description("authentication for opt")
public static class ApiOperatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/opt/**");
http
.authorizeRequests()
.antMatchers("/opt/internal/authenticate").permitAll()
.antMatchers("/opt/main/v1/**").permitAll()
.antMatchers("/opt/main/v2/ex").permitAll()
.antMatchers("/opt/main/v3/ex").permitAll()
.antMatchers("/opt/main/v3/ex").permitAll()
.antMatchers("/opt/v4").permitAll()
.antMatchers("/opt/**").hasRole("OPERATOR")
.anyRequest().authenticated()
.accessDecisionManager(accessDecisionManager())
.and()
.csrf().disable()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint());
http
.authenticationProvider(operatorsAuthenticationProvider())
//.authenticationProvider(operatorsJwtAuthenticationProvider())
.authenticationProvider(googleAuthenticationProvider());
.sessionManagement().sessionCreationPolicy(STATELESS);
http.addFilterBefore(jwtRequestFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public AuthenticationProvider operatorsAuthenticationProvider() {
return new OperatorsAuthenticationProviderImpl();
}
@Bean
public JwtRequestFilter jwtRequestFilter(){
return new JwtRequestFilter();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public JwtUtil jwtUtil() throws Exception{
return new JwtUtil();
}
@Bean
public AuthenticationProvider operatorsJwtAuthenticationProvider() {
return new OperatorsJwtAuthenticationProviderImpl();
}
@Bean
public AuthenticationProvider googleAuthenticationProvider() {
return new GoogleAuthenticationProviderImpl();
}
@Bean
protected AuthenticationEntryPoint authenticationEntryPoint() {
OperatorsRestAuthenticationEntryPoint authenticationEntryPoint = new OperatorsRestAuthenticationEntryPoint();
authenticationEntryPoint.setRealmName("operators");
return authenticationEntryPoint;
}
}
在我的控制器中,我创建了 /authenticate
端点并自动连接authenticationmanager
@Autowired
AuthenticationManager authenticationManager;
@Autowired
JwtUtil jwtUtil;
@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtAuthenticationRequest authenticationRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);
}
catch (BadCredentialsException e) {
throw new Exception("Incorrect username or password", e);
}
final UserDetails userDetails = authenticationService
.loadUserByUsername(authenticationRequest.getUsername());
final String jwt = jwtUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
JWT过滤器
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
OperatorsAuthenticationService authenticationService;
@Autowired
JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.authenticationService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}else{
throw new Exception("Auth error");
}
}
filterChain.doFilter(request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
现在的问题是:当我尝试使用基本身份验证进行身份验证时,系统递归地调用 operatorsAuthenticationProvider
最后我得到了一个 java.lang.StackOverflowError
```
java.lang.StackOverflowError
at java.lang.reflect.Method.specificToStringHeader(Method.java:369)
at java.lang.reflect.Executable.sharedToString(Executable.java:121)
at java.lang.reflect.Method.toString(Method.java:361)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at org.springframework.remoting.rmi.RmiClientInterceptor.doInvoke(RmiClientInterceptor.java:357)
at org.springframework.remoting.rmi.RmiClientInterceptor.invoke(RmiClientInterceptor.java:260)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy363.authenticate(Unknown Source)
at sun.reflect.GeneratedMethodAccessor230.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy364.authenticate(Unknown Source)
at com.mydom.myapp.api.security.defaultimpl.OperatorsAuthenticationProviderImpl.authenticate(OperatorsAuthenticationProviderImpl.java:33)
我注意到,在注解authenticationmanagerbeanbean的声明时,basic auth与以前一样工作正常。
有人知道发生了什么吗?authenticationmanagerbean声明有什么问题?谢谢
暂无答案!
目前还没有任何答案,快来回答吧!