錯(cuò)誤:User must be authenticated with Spring Security before authorization can be completed.
簡(jiǎn)要說(shuō)下錯(cuò)誤原因:
該錯(cuò)誤是oauth2 授權(quán)碼模式 訪問(wèn)接口 /oauth/authorize 時(shí)仪召,由于SpringSecurity上下文中沒(méi)有用戶(hù)認(rèn)證信息曙搬,所以在spring security 中攔截報(bào)錯(cuò)。源碼中 在/oauth/authorize 接口debug可以看到問(wèn)題端蛆。
處理方法
錯(cuò)誤信息已經(jīng)說(shuō)明了是要先認(rèn)證秉氧,所以我們?cè)谡?qǐng)求/oauth/authorize接口前認(rèn)證用戶(hù)信息即可
1.1 通過(guò)寫(xiě)一個(gè)用戶(hù)身份過(guò)濾器進(jìn)行處理掖看,代碼如下
@Component
@Slf4j
public class TokenAuthenticationFilter extends OncePerRequestFilter {
@Autowired
AuthenticationManager authenticationManager;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader(HttpConstant.Header.AUTHENTICATION);
if (StrUtil.isBlank(token)) {
//解析請(qǐng)求參數(shù) request.getQueryString()
Map<String, String> parameters = HttpParamUtil.getRequestParameters(request);
if (SecurityConstants.AUTH_CODE_URL.equals(request.getRequestURI())&¶meters.size()>0) {
//參數(shù)中提取 clientId 捕发,根據(jù)client 數(shù)據(jù)庫(kù)查詢(xún)用戶(hù)信息進(jìn)行身份認(rèn)證
String clientId = parameters.get("client_id");
//根據(jù)clientId查詢(xún)用戶(hù)信息 省略數(shù)據(jù)庫(kù)用戶(hù)信息查詢(xún)
String username="admin";
String password="123456";
//賬號(hào)密碼認(rèn)證
JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(username, password);
// 通過(guò)authenticationManager
Authentication authentication = authenticationManager.authenticate(jwtAuthenticationToken);
// 認(rèn)證成功后將認(rèn)證信息存儲(chǔ)到SpringSecurity上下文中
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
//在過(guò)濾器中添加return锁右,是為了結(jié)束該方法的運(yùn)行失受,也就是結(jié)束過(guò)濾
return;
}
}
}
方法邏輯說(shuō)明:用戶(hù)請(qǐng)求中沒(méi)有認(rèn)證令牌token,判斷請(qǐng)求路徑是否是/oauth/authorize,如果是同時(shí)判斷是否攜帶有參數(shù)(攜帶參數(shù)是請(qǐng)求授權(quán)的請(qǐng)求咏瑟,生成授權(quán)碼的請(qǐng)求沒(méi)有參數(shù))拂到,如果有則解析參數(shù)獲取cliend_id,根據(jù)cliend_id 查詢(xún)數(shù)據(jù)庫(kù)該用戶(hù)信息码泞,完成spring security 的鑒權(quán)并將認(rèn)證信息息存儲(chǔ)到SpringSecurity上下文中
1.2 spring security 核心配置類(lèi)中 注冊(cè)以上的過(guò)濾器兄旬,并指定該過(guò)濾器為spring security 一系列過(guò)濾器的首個(gè)過(guò)濾器
1.3 spring security 核心配置類(lèi)中 對(duì) 以 /oauth 開(kāi)頭的請(qǐng)求放行
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private MyLogOutSuccessHandler logOutSuccessHandler;
@Autowired
private SimpleAccessDeniedHandler simpleAccessDeniedHandler;
@Autowired
private SimpleAuthenticationEntryPoint simpleAuthenticationEntryPoint;
@Autowired
private TokenAuthenticationFilter tokenAuthenticationFilter;
/**
* http請(qǐng)求攔截認(rèn)證
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 禁用 csrf, 由于使用的是JWT,我們這里不需要csrf
http.csrf().disable();
http.authorizeRequests()
.and()
.formLogin() //新增login form支持用戶(hù)登錄及授權(quán)
//自定義登錄頁(yè)面
.loginPage("http://localhost:3000/login")
//認(rèn)證成功跳轉(zhuǎn)的頁(yè)面
.successForwardUrl("http://localhost:3000/index")
.failureForwardUrl("http://localhost:3000/login")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("http://localhost:3000/login")
.logoutSuccessHandler(logOutSuccessHandler)
.permitAll()
//wagger 相關(guān)請(qǐng)求放行
.and()
.authorizeRequests()
//oauth相關(guān)請(qǐng)求放行
.antMatchers("/oauth/**").permitAll()
.antMatchers("/login/**").permitAll()
.antMatchers("/logout/**").permitAll()
.antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources").permitAll()
.antMatchers("/swagger-ui/*").permitAll()
.antMatchers("/v2/api-docs").permitAll()
.antMatchers("/v3/api-docs").permitAll()
.antMatchers("/webjars/**").permitAll()
// 其他所有請(qǐng)求需要身份認(rèn)證
.anyRequest().authenticated()
//配置自定義異常處理
.and().exceptionHandling()
.accessDeniedHandler(simpleAccessDeniedHandler)
.authenticationEntryPoint(simpleAuthenticationEntryPoint);
// 添加自定義 過(guò)濾器 余寥,放置在身份認(rèn)證過(guò)濾器前
http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) {
// 使用自定義登錄身份認(rèn)證組件
auth.authenticationProvider(new JwtAuthenticationProvider(userDetailsService));
}
/**
* AuthenticationManager對(duì)象在OAuth2認(rèn)證服務(wù)中要使用领铐,提前放入IOC容器中
*/
@Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}