前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OAuth2.0通过token获取受保护资源的解析

OAuth2.0通过token获取受保护资源的解析

作者头像
算法之名
发布2019-08-20 17:26:45
1.1K0
发布2019-08-20 17:26:45
举报
文章被收录于专栏:算法之名算法之名

假设我们的accessToken是这样的

{ "access_token": "ffb71ed0-5e48-44bc-b4aa-16ee0ba24b01", "token_type": "bearer", "refresh_token": "70220a36-3419-4c48-a60e-2d80b0f1774f", "expires_in": 28799, "scope": "app" }

获取当前用户的Authentication,Authentication是一个接口,具体实现类是OAuth2Authentication,由以下返回结果可知,他是一个包含了大量信息的类.而每一个信息块里面又有着接口和实现类.

代码语言:javascript
复制
@GetMapping("/user-me")
public Authentication principal() {
   Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
   log.debug("user-me:{}", authentication.getName());
   return authentication;
}

请求http://127.0.0.1:50075/user-me?access_token=ffb71ed0-5e48-44bc-b4aa-16ee0ba24b01

返回结果如下

{ "authorities": [ { "authority": "back:menu:set2role" }, { "authority": "mail:update" }, { "authority": "back:permission:delete" }, { "authority": "role:permission:byroleid" }, { "authority": "back:menu:save" }, { "authority": "back:menu:query" }, { "authority": "ip:black:query" }, { "authority": "ip:black:save" }, { "authority": "file:del" }, { "authority": "ip:black:delete" }, { "authority": "mail:query" }, { "authority": "back:user:query" }, { "authority": "back:role:permission:set" }, { "authority": "sms:query" }, { "authority": "back:role:query" }, { "authority": "back:permission:query" }, { "authority": "back:role:save" }, { "authority": "back:user:role:set" }, { "authority": "log:query" }, { "authority": "file:query" }, { "authority": "back:menu:update" }, { "authority": "back:role:update" }, { "authority": "back:role:delete" }, { "authority": "back:user:password" }, { "authority": "ROLE_SUPER_ADMIN" }, { "authority": "back:menu:delete" }, { "authority": "back:user:update" }, { "authority": "menu:byroleid" }, { "authority": "mail:save" }, { "authority": "user:role:byuid" }, { "authority": "back:permission:save" }, { "authority": "back:permission:update" } ], "details": { "remoteAddress": "127.0.0.1", "sessionId": null, "tokenValue": "ffb71ed0-5e48-44bc-b4aa-16ee0ba24b01", "tokenType": "Bearer", "decodedDetails": null }, "authenticated": true, "userAuthentication": { "authorities": [ { "authority": "back:menu:set2role" }, { "authority": "mail:update" }, { "authority": "back:permission:delete" }, { "authority": "role:permission:byroleid" }, { "authority": "back:menu:save" }, { "authority": "back:menu:query" }, { "authority": "ip:black:query" }, { "authority": "ip:black:save" }, { "authority": "file:del" }, { "authority": "ip:black:delete" }, { "authority": "mail:query" }, { "authority": "back:user:query" }, { "authority": "back:role:permission:set" }, { "authority": "sms:query" }, { "authority": "back:role:query" }, { "authority": "back:permission:query" }, { "authority": "back:role:save" }, { "authority": "back:user:role:set" }, { "authority": "log:query" }, { "authority": "file:query" }, { "authority": "back:menu:update" }, { "authority": "back:role:update" }, { "authority": "back:role:delete" }, { "authority": "back:user:password" }, { "authority": "ROLE_SUPER_ADMIN" }, { "authority": "back:menu:delete" }, { "authority": "back:user:update" }, { "authority": "menu:byroleid" }, { "authority": "mail:save" }, { "authority": "user:role:byuid" }, { "authority": "back:permission:save" }, { "authority": "back:permission:update" } ], "details": { "grant_type": "password", "scope": "app", "client_secret": "system", "client_id": "system", "username": "admin|USERNAME" }, "authenticated": true, "principal": { "id": 1, "username": "admin", "password": "$2a$10$3uOoX1ps14CxuotogUoDreW8zXJOZB9XeGdrC/xDV36hhaE8Rn9HO", "nickname": "测试1", "headImgUrl": "", "phone": "", "sex": 1, "enabled": true, "type": "APP", "createTime": "2018-01-17T16:56:59.000+0000", "updateTime": "2018-01-17T16:57:01.000+0000", "sysRoles": [ { "id": 1, "code": "SUPER_ADMIN", "name": "超级管理员", "createTime": "2018-01-19T20:32:16.000+0000", "updateTime": "2018-01-19T20:32:18.000+0000" } ], "permissions": [ "back:menu:set2role", "mail:update", "back:permission:delete", "role:permission:byroleid", "back:menu:save", "back:menu:query", "ip:black:query", "ip:black:save", "file:del", "ip:black:delete", "mail:query", "back:user:query", "back:role:permission:set", "sms:query", "back:role:query", "back:permission:query", "back:role:save", "back:user:role:set", "log:query", "file:query", "back:menu:update", "back:role:update", "back:role:delete", "back:user:password", "back:menu:delete", "back:user:update", "menu:byroleid", "mail:save", "user:role:byuid", "back:permission:save", "back:permission:update" ], "credentialsNonExpired": true, "accountNonExpired": true, "accountNonLocked": true }, "credentials": null, "name": "admin" }, "credentials": "", "principal": { "id": 1, "username": "admin", "password": "$2a$10$3uOoX1ps14CxuotogUoDreW8zXJOZB9XeGdrC/xDV36hhaE8Rn9HO", "nickname": "测试1", "headImgUrl": "", "phone": "", "sex": 1, "enabled": true, "type": "APP", "createTime": "2018-01-17T16:56:59.000+0000", "updateTime": "2018-01-17T16:57:01.000+0000", "sysRoles": [ { "id": 1, "code": "SUPER_ADMIN", "name": "超级管理员", "createTime": "2018-01-19T20:32:16.000+0000", "updateTime": "2018-01-19T20:32:18.000+0000" } ], "permissions": [ "back:menu:set2role", "mail:update", "back:permission:delete", "role:permission:byroleid", "back:menu:save", "back:menu:query", "ip:black:query", "ip:black:save", "file:del", "ip:black:delete", "mail:query", "back:user:query", "back:role:permission:set", "sms:query", "back:role:query", "back:permission:query", "back:role:save", "back:user:role:set", "log:query", "file:query", "back:menu:update", "back:role:update", "back:role:delete", "back:user:password", "back:menu:delete", "back:user:update", "menu:byroleid", "mail:save", "user:role:byuid", "back:permission:save", "back:permission:update" ], "credentialsNonExpired": true, "accountNonExpired": true, "accountNonLocked": true }, "clientOnly": false, "oauth2Request": { "clientId": "system", "scope": [ "app" ], "requestParameters": { "grant_type": "password", "scope": "app", "client_id": "system", "username": "admin|USERNAME" }, "resourceIds": [], "authorities": [], "approved": true, "refresh": false, "redirectUri": null, "responseTypes": [], "extensions": {}, "grantType": "password", "refreshTokenRequest": null }, "name": "admin" }

首先我们需要写一个资源配置类

代码语言:javascript
复制
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter

其中这个@EnableResourceServer实际上帮我们加入了一个过滤器(应该说所有的业务模块都要有一个资源配置类来开启这个过滤器)org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter

在他的源码中有一个doFilter

代码语言:javascript
复制
public class OAuth2AuthenticationProcessingFilter implements Filter, InitializingBean
代码语言:javascript
复制
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
代码语言:javascript
复制
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    boolean debug = logger.isDebugEnabled();
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;

    try {
        //从request中解析PreAuthenticatedAuthenticationToken(注意这里并不是OAuth2Authentication)
        Authentication authentication = this.tokenExtractor.extract(request);
        if(authentication == null) {
            if(this.stateless && this.isAuthenticated()) {
                if(debug) {
                    logger.debug("Clearing security context.");
                }

                SecurityContextHolder.clearContext();
            }

            if(debug) {
                logger.debug("No token in request, will continue chain.");
            }
        } else {
            request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
            if(authentication instanceof AbstractAuthenticationToken) {
                AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken)authentication;
                needsDetails.setDetails(this.authenticationDetailsSource.buildDetails(request));
            }
            
            Authentication authResult = this.authenticationManager.authenticate(authentication);
            if(debug) {
                logger.debug("Authentication success: " + authResult);
            }

            this.eventPublisher.publishAuthenticationSuccess(authResult);
            //此处为把authResult放入安全容器中,此处比较重要
            SecurityContextHolder.getContext().setAuthentication(authResult);
        }
    } catch (OAuth2Exception var9) {
        SecurityContextHolder.clearContext();
        if(debug) {
            logger.debug("Authentication request failed: " + var9);
        }

        this.eventPublisher.publishAuthenticationFailure(new BadCredentialsException(var9.getMessage(), var9), new PreAuthenticatedAuthenticationToken("access-token", "N/A"));
        this.authenticationEntryPoint.commence(request, response, new InsufficientAuthenticationException(var9.getMessage(), var9));
        return;
    }

    chain.doFilter(request, response);
}

TokenExtractor也是一个接口,我们可以看到,他是由BearerTokenExtractor实现类来实现的.基本上BearerTokenExtractor整个对象的方法都有调用.

代码语言:javascript
复制
public class BearerTokenExtractor implements TokenExtractor {
    private static final Log logger = LogFactory.getLog(BearerTokenExtractor.class);

    public BearerTokenExtractor() {
    }

    public Authentication extract(HttpServletRequest request) {
        String tokenValue = this.extractToken(request);
        if(tokenValue != null) {
            PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(tokenValue, "");
            return authentication;
        } else {
            return null;
        }
    }
    /**
     *从request参数查找认证
    */
    protected String extractToken(HttpServletRequest request) {
        String token = this.extractHeaderToken(request);
        if(token == null) {
            logger.debug("Token not found in headers. Trying request parameters.");
            token = request.getParameter("access_token");
            if(token == null) {
                logger.debug("Token not found in request parameters.  Not an OAuth2 request.");
            } else {
                request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, "Bearer");
            }
        }

        return token;
    }
    /**
     *从request的header开始查找认证
    */
    protected String extractHeaderToken(HttpServletRequest request) {
        Enumeration headers = request.getHeaders("Authorization");

        String value;
        do {
            if(!headers.hasMoreElements()) {
                return null;
            }

            value = (String)headers.nextElement();
        } while(!value.toLowerCase().startsWith("Bearer".toLowerCase()));

        String authHeaderValue = value.substring("Bearer".length()).trim();
        request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, value.substring(0, "Bearer".length()).trim());
        int commaIndex = authHeaderValue.indexOf(44);
        if(commaIndex > 0) {
            authHeaderValue = authHeaderValue.substring(0, commaIndex);
        }

        return authHeaderValue;
    }
}

SecurityContextHolder.getContext().setAuthentication(authResult);我们单独把这个提取出来,在之前的

代码语言:javascript
复制
@GetMapping("/user-me")
public Authentication principal() {
   Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
   log.debug("user-me:{}", authentication.getName());
   return authentication;
}

我们可以看到我们访问的登录验证用户是从SecurityContextHolder.getContext().getAuthentication()提取出来的.

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档