spring-security5 oauth 开箱既用,认证时返回的是oauth2 标准的返回值
先来看看,默认的返回值
## 未认证
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
## 认证失败
{
"error": "invalid_client",
"error_description": "Bad client credentials"
}
## 认证成功
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IndlYmFwcCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sIkF1dGhvciI6InhpYW95YW8iLCJleHAiOjE1NjEwMjYxNDAsImF1dGhvcml0aWVzIjpbIlJPTEVfVEVTVCIsIlJPTEVfT0FVVEhfQURNSU4iXSwianRpIjoiYzhlMTA3YTItYWFlZS00NWNhLTk1NWYtM2UyZDgwZjhlZmM2IiwiY2xpZW50X2lkIjoid2ViYXBwIiwidGltZXN0YW1wIjoiMTU2MTAxODk0MDEwOCJ9.VgL4voycGdn4Fm_Ij3ZeAc9Rxdsmb_IXR14lgtJh1KE",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IndlYmFwcCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImM4ZTEwN2EyLWFhZWUtNDVjYS05NTVmLTNlMmQ4MGY4ZWZjNiIsIkF1dGhvciI6InhpYW95YW8iLCJleHAiOjE1NjIzMTQ5NDAsImF1dGhvcml0aWVzIjpbIlJPTEVfVEVTVCIsIlJPTEVfT0FVVEhfQURNSU4iXSwianRpIjoiMTRkNmQyMmMtZjc4OS00YzdlLTk0YjAtNTliYzhmYjlmY2NiIiwiY2xpZW50X2lkIjoid2ViYXBwIiwidGltZXN0YW1wIjoiMTU2MTAxODk0MDEwOCJ9.SOch9UKE078fRSxHYDZzXVTelVH3gpFOZUzM3KelKrk",
"expires_in": 7199,
"scope": "all",
"Author": "xiaoyao",
"clientId": "webapp",
"timestamp": "1561018940108",
"jti": "c8e107a2-aaee-45ca-955f-3e2d80f8efc6"
}
然而,这些虽是标准协议返回字段,公开给第三方做接口是没啥问题,但这个也会给我们前端去使用,这时候就会对前端造成一定的麻烦,在业务层,前端,一般会写一个拦截器,去通过一个code 或status字段,进行拦截,而认证阶段返回值就不统一了,所以我希望对于整个系统都是的,即看一下下面的期望结果的例子:
// code,msg,data 三个是我们与前端协定的整个系统中的返回值
# 未认证
{
"msg": "token不正确或已过期",
"code": "401",
"data": "Full authentication is required to access this resource",
// 下面两个是原始返回值
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
## 服务器错误
{
"error": "invalid_request",
"code": 400,
"msg": "Internal Server Error",
"error_description": "Internal Server Error",
"data": ""
}
// 登陆成功
{
"code": "200",
"msg": "success",
"data":{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IndlYmFwcCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sIkF1dGhvciI6InhpYW95YW8iLCJleHAiOjE1NjEwMjYxNDAsImF1dGhvcml0aWVzIjpbIlJPTEVfVEVTVCIsIlJPTEVfT0FVVEhfQURNSU4iXSwianRpIjoiYzhlMTA3YTItYWFlZS00NWNhLTk1NWYtM2UyZDgwZjhlZmM2IiwiY2xpZW50X2lkIjoid2ViYXBwIiwidGltZXN0YW1wIjoiMTU2MTAxODk0MDEwOCJ9.VgL4voycGdn4Fm_Ij3ZeAc9Rxdsmb_IXR14lgtJh1KE",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IndlYmFwcCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImM4ZTEwN2EyLWFhZWUtNDVjYS05NTVmLTNlMmQ4MGY4ZWZjNiIsIkF1dGhvciI6InhpYW95YW8iLCJleHAiOjE1NjIzMTQ5NDAsImF1dGhvcml0aWVzIjpbIlJPTEVfVEVTVCIsIlJPTEVfT0FVVEhfQURNSU4iXSwianRpIjoiMTRkNmQyMmMtZjc4OS00YzdlLTk0YjAtNTliYzhmYjlmY2NiIiwiY2xpZW50X2lkIjoid2ViYXBwIiwidGltZXN0YW1wIjoiMTU2MTAxODk0MDEwOCJ9.SOch9UKE078fRSxHYDZzXVTelVH3gpFOZUzM3KelKrk",
"expires_in": 7199,
"scope": "all",
"Author": "xiaoyao",
"clientId": "webapp",
"timestamp": "1561018940108",
"jti": "c8e107a2-aaee-45ca-955f-3e2d80f8efc6"
}
}
知道自己希望想要什么,才好着手去撸代码
这里面要分两步走,因为资源服务 和认证服务中是不一样的(刚开始做的我以为是一样的,后开跟了半天的源代码,才走出了这个坑)默认的主要有两个类`OAuth2AuthenticationEntryPoint` 和 `DefaultWebResponseExceptionTranslator`
//资源服务上,默认是交给这个类来处理的,包括未认证的,和错误的token
//认证服务器上未认证的也是这个
`OAuth2AuthenticationEntryPoint`
//认证服务器上,上面的类不够用了,因为在security的过滤器中到处出现了
private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
和
private WebResponseExceptionTranslator exceptionTranslator = new DefaultWebResponseExceptionTranslator();
// 认证时产生的异常交给了这个类处理返回结果
`DefaultWebResponseExceptionTranslator`
先来看资源服务吧,这个比较简单
首先自定义一个`AuthenticationEntryPoint`
public class CustomAuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {
private WebResponseExceptionTranslator> exceptionTranslator = new DefaultWebResponseExceptionTranslator();
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
// super () //doHandle(request, response, e)
// log.debug("token 验证失败处理器")
BaseResult
然后在资源费器的配置中配置即可
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Autowired
private BaseAuthenticationConfig baseAuthenticationConfig;
@Autowired(required = false)
private AuthenticationEntryPoint authenticationEntryPoint;
@Override
public void configure(HttpSecurity http) throws Exception {
log.debug("Using ResourceServerConfiguration configure(HttpSecurity). " );
baseAuthenticationConfig.configure(http);
http
.authorizeRequests()
.antMatchers(baseAuthenticationConfig.getIgnoreUrl()).permitAll()
.antMatchers("/login","/error","/oauth/token",
"/process/login","/logout","/login.html").permitAll()
// .antMatchers("/**").authenticated()
.antMatchers("/sys/sys-user/info").authenticated()
.and()
.csrf().disable()
;
}
// 在这儿配
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// 配置认证失败处理器
if(authenticationEntryPoint != null){
resources.authenticationEntryPoint(authenticationEntryPoint);
}
}
}
认证服务器,稍微麻烦点
首先将自定义的 `AuthenticationEntryPoint`在http config中配置上
public void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
.loginProcessingUrl("/user/login")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.and()
.authorizeRequests().antMatchers("/login","/user/login","/user/logout","/oauth/token").permitAll()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
.and()
// 这里设置的 只是用户没有登录的 情况下 没有token 的情况下,异常给下面的处理
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
;
}
好了在登录时认证失败会产生各种异常,但最有都有一个`ExceptionTranslationFilter`处理,这个是spring security的基础知识(插张图)
这个是访问认证服务器 资源的时候,还是交给自定义的AuthenticationEntryPoint 处理的
看一下登陆的时候
登陆失败
-- 未完 待续,主要原因在用户通过授权码登陆异常时没有走自定义的异常处理类