SpringCloud+Spring Security+OAuth2授权登录(三)

上回书说到,认证服务器为了美观和良好的用户体验,修改了登录页面和返回登录失败的信息。本回就讲讲,认证客户端或者称之为资源服务器自定义权限认证和为移动端做的修改。

一般来说,像通过微信授权登录的系统都是有一套自己的权限体系的,微信那边只是拿到一个用户的信息。所以这里判断用户对应自己系统内部的权限需要开发者自行解决。

@Component
public class CustomPermission {
    /**
     * permission 获取接口设置的权限
     */
    public boolean hasPermission(String permission) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(" >>> username : " + authentication.getPrincipal());
        //TODO: 获取账号通过本系统数据库获取对应的权限进行逻辑处理
        return true;
    }
}

对应Controller使用

@PreAuthorize("@customPermission.hasPermission('SUPERADMIN')")
    @RequestMapping("/byeuser")
    public String byeUser() {
        return "bye user!";
    }

其次,返回错误信息,系统默认返回的都是英文单词和状态码,不接地气。那么还是要转化一下,至少看的更直白一点。

@Component
public class AuthExceptionHandler implements AuthenticationEntryPoint, AccessDeniedHandler {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException authException) throws IOException, ServletException {
        Throwable cause = authException.getCause();
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        // CORS "pre-flight" request
        httpServletResponse.addHeader("Access-Control-Allow-Origin", "*");
        httpServletResponse.addHeader("Cache-Control", "no-cache");
        httpServletResponse.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with");
        httpServletResponse.addHeader("Access-Control-Max-Age", "1800");
        if (cause instanceof InvalidTokenException) {
            //Token无效
            JSONObject res = new JSONObject();
            res.put("error", "ACCESS_TOKEN_INVALID");
            res.put("error_description", "Token无效");
            httpServletResponse.getWriter().write(res.toJSONString());
        } else {
            //资源未授权
            JSONObject res = new JSONObject();
            res.put("error", "UNAUTHORIZED");
            res.put("error_description", "资源未授权");
            httpServletResponse.getWriter().write(res.toJSONString());
        }
    }

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException accessException) throws IOException, ServletException {
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        httpServletResponse.addHeader("Access-Control-Allow-Origin", "*");
        httpServletResponse.addHeader("Cache-Control", "no-cache");
        httpServletResponse.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with");
        httpServletResponse.addHeader("Access-Control-Max-Age", "1800");
        //访问资源的用户权限不足
        JSONObject res = new JSONObject();
        res.put("error", "INSUFFICIENT_PERMISSIONS");
        res.put("error_description", "访问资源的用户权限不足");
        httpServletResponse.getWriter().write(res.toJSONString());
    }
}

第三,使用Header的方式对App不是很友好,所以我们需要认可在URL后面拼接token的方式,emmm…本菜鸟一开始工作的时候做的App就是URL后面拼接token的。

@Component
@RequiredArgsConstructor
public class BearerTokenExtractor implements TokenExtractor {

    @Override
    public Authentication extract(HttpServletRequest request) {
        String tokenValue = extractToken(request);
        if (tokenValue != null) {
            return new PreAuthenticatedAuthenticationToken(tokenValue, "");
        }
        return null;
    }

    protected String extractToken(HttpServletRequest request) {
        String token = extractHeaderToken(request);
        if (token == null) {
            //从requestParameter中获取token
            token = request.getParameter("token");
        }
        return token;
    }

    protected String extractHeaderToken(HttpServletRequest request) {
        Enumeration<String> headers = request.getHeaders("Authorization");
        while (headers.hasMoreElements()) {
            //从Header中获取token
            String token = headers.nextElement();
            if (token != null && token.startsWith("Bearer ")) {
                return token.substring(7);
            }
        }
        return null;
    }
}

第二和第三条使我们自定义的方法生效的代码如下:

@Override
    public void configure(ResourceServerSecurityConfigurer endpoints) {
        endpoints
                .accessDeniedHandler(authExceptionHandler)
                .authenticationEntryPoint(authExceptionHandler)
                .tokenStore(tokenStore)
                .tokenExtractor(tokenExtractor)
                .resourceId("resource_id")
                .tokenServices(tokenService());
    }

DEMO:https://gitee.com/ichampion/Public-Project-Demo.git


至此,这一套系统基本可用了,本菜鸟还测试了一下统一登出,也是可以实现的。当然了,对于大量的系统接入,那么认证服务器是否要做集群化,session如何共享,这些都要在往后的工作中去探索。祝各位开发者小伙伴工作顺利!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值