由于最近遇到了新问题,还折磨了我两天,所以这里就简单的记录一下⑧

错误集中处理

由于系统需要,所以为项目添加了一个错误集中处理配置
前情提要

spring boot : 2.0.3.RELEASE

参考文章

首先根据spring boot版本的不同,以1.4.0为界是有不同的配置方式的,这里由于用的是2.0.3.RELEASE,所以配置如下。

package com.yubotao.springsecurityoauth2.config;

import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;



/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 17:23 2018/10/10
 * @Modified By:
 */
@Component
public class ErrorPageConfig implements ErrorPageRegistrar {

    @Override
    public void registerErrorPages(ErrorPageRegistry registry){
        ErrorPage[] errorPages = new ErrorPage[]{
                new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
                new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"),
                new ErrorPage(Throwable.class, "/error/500")
        };
        registry.addErrorPages(errorPages);

    }

}

接着加一个进行跳转的Controller

package com.yubotao.springsecurityoauth2.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 17:17 2018/10/10
 * @Modified By:
 */
@Controller
@RequestMapping("/error")
public class ErrorController {

    @RequestMapping(value = "/401", method = RequestMethod.GET)
    public String error_401(){
        return "error_401";
    }

    @RequestMapping(value = "/404", method = RequestMethod.GET)
    public String error_404(){
        return "error_404";
    }

    @RequestMapping(value = "/500", method = RequestMethod.GET)
    public String error_500(){
        return "error_500";
    }

}

然后放一个页面吧,其他的都一样
error_404.html

<!DOCTYPE html>
<html>
<head>

</head>

<body>
<h1>404</h1>
<h3>抱歉,无法找到网页</h3>
</body>
</html>

spring security oauth 2 的401处理(invalid_token)

开始我们使用了上面的那种异常集中处理,我开始以为可以把401错误也纳入其中,但是经过尝试之后发现这种方式是行不通的,因为当出现401报错的时候,返回的是一个xml形式的,总之就很烦,然后这个地方我用了1天的时间才找到比较好的解决办法。

spring oauth2 server spring oauth2 server 0.41_重定向

最开始的想法是用过滤器,应该是可以做的,但是我没做,一个是网上相关资料较少,再就是想看看有没有其他的方式解决。

后来用尝试了使用对oauth 2的资源服务器进行配置登陆页面的方式,就是如下这种方式

@Override
        public void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                    .and()
                    .requestMatchers().anyRequest()
                    .and()
                    .anonymous()
                    .and()
                    .formLogin().loginPage("/login")
                    .and()
                    .authorizeRequests()
                    // or可以通过access_token访问,但是and不行;经过测试,应该是hasRole()方法出了问题,这里无法通过
//                    .antMatchers("/product/**").access("#oauth2.hasScope('select') and  hasRole('ROLE_ADMIN')")
                    .antMatchers("/order/**").authenticated();  // 配置order访问控制,必须认证过后才可以访问
            // @formatter:on
        }

这种情况貌似是失败了,无奈之下,只能继续寻找解决方法,果然让我找到了,可以看到这个文章,虽然效果不是我想要的,但是是可以借鉴的。

所以其实我们只需要定义一个AuthExceptionEntryPoint

package com.yubotao.springsecurityoauth2.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Auther: yubt
 * @Description:
 * @Date: Created in 11:36 2018/10/11
 * @Modified By:
 */
public class AuthExceptionEntryPoint implements AuthenticationEntryPoint {

    @Value("${server.url}")
    private  String serverUrl;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException)
            throws ServletException {

//        Map map = new HashMap();
//        map.put("error", "401");
//        map.put("message", "token过期,请重新登陆");
//        map.put("path", request.getServletPath());
//        map.put("timestamp", String.valueOf(new Date().getTime()));
//        response.setContentType("application/json");
//        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

        try {
//            ObjectMapper mapper = new ObjectMapper();
//            mapper.writeValue(response.getOutputStream(), map);
            System.out.println("进入Entry方法");
            response.sendRedirect(serverUrl + "/error/401");
        } catch (Exception e) {
            throw new ServletException();
        }
    }

}

开始的时候我以为这种方式也不行,因为我没有注掉那些代码,就进行了重定向,结果还是报错了,并且发现这个方法进入了两次,然后同事说了依据可能是response.setContentType("application/json");这句的问题,然后我就把上面的代码注掉,果然就可以正确重定向了。

这样,我们就把这个invalid_token的问题给解决了。

效果如下

当我请求http://localhost:8080/order/1时,由于需要权限,但是我并没有登陆,所以就会重定向到这个页面

spring oauth2 server spring oauth2 server 0.41_重定向_02

而关于AuthenticationEntryPoint的相关文章,我只能找到这样一篇文章,稍微提到了一下,也没去官方文档去查,不过大概了解它的功能了,就是一个认证系统,不过还是存疑,以后有机会可以再去官方文档搜搜。

赶着下班,今天就草草写到这了。

Github项目地址