OAuth2.0协议-授权码模式介绍及案例应用

一、关于OAuth2.0

1.什么是OAuth2.0

OAuth2.0(开放授权)是一个关于授权的开放的网络协议。

2.OAuth2.0的作用是什么?

让客户端安全可控地获取用户的授权,与服务提供商之间进行交互。可以免去用户同步的麻烦,同时也增加了用户信息的安全。

3.设计理念

OAuth在第三方应用与服务提供商之间设置了一个授权层。第三方应用不能直接登录服务提供商,只能登录授权层,以此将用户与客户端区分开来。第三方应用登录授权层所用的令牌,与用户的密码不同。用户可以在登录授权的时候,指定授权层令牌的权限范围和有效期。 

第三方应用登录授权层以后,服务提供商根据令牌的权限范围和有效期,向第三方应用开放用户资源。

4.应用场景

实现第三方应用的接口访问控制。第三方应用登录。

比如:

5.运行流程

(A)用户打开客户端以后,客户端要求用户给予授权。

(B)用户同意给予客户端授权。

(C)客户端使用上一步获得的授权,向认证服务器申请令牌。

(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。

(E)客户端使用令牌,向资源服务器申请获取资源。

(F)资源服务器确认令牌无误,同意向客户端开放资源。

6.客户端的授权模式

客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。

  • 授权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

二、授权码模式

授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

下面是上面这些步骤所需要的参数。

A步骤中,客户端申请认证的URI,包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为"code"
  • client_id:表示客户端的ID,必选项
  • redirect_uri:表示重定向URI,可选项
  • scope:表示申请的权限范围,可选项
  • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

三、授权码模式代码(基于springboot)

应用案例运行流程

应用B的pom.xml需要添加oauth2的相关依赖:

<!--oauth2相关依赖 -->
<dependency>
    <groupId>org.apache.oltu.oauth2</groupId>
    <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>org.apache.oltu.oauth2</groupId>
    <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>org.apache.oltu.oauth2</groupId>
    <artifactId>org.apache.oltu.oauth2.common</artifactId>
    <version>1.0.0</version>
</dependency>

应用A的配置文件application.properties:

#应用id和秘钥  用于注册表示用户  获取token值会用到
client.ak = c3c31e1e-2117-4eee-8ca6-011594675ef1
client.sk = 5143faab-67e6-48aa-acf2-8e716b4b5983

client.oauth.server.host = hostname  #服务B的ip地址
client.oauth.server.token.url = http://${client.oauth.server.host}/accessToken
client.oauth.server.redirect.url = http://127.0.0.1:8080

 

3.1 浏览器获取应用B端的code值

1.前端js发起请求

MC.http.ajax({
    async: false,
    url: clientUrl + "/remote/siti/test?callback=?",
    data: "",
    timeout: 500,
    dataType: 'jsonp',
    type: 'get',
    success: function (rsp) {
        if (null != rsp && rsp.result == "ok") {
            isTestConnect = true;
            MC.cookie.set("isConnection", "1");
            var code = null;
            var busType = null;
            MC.http.ajax({
                url: '/teacher/user/code',
                dataType: 'json',
                type: 'get',
                async: false,
                success: function (rsp) {
                    // alert(rsp);
                    code = rsp.code;
                },
                error: function () {
                    MC.msg('alert', '获取code失败,请稍后再试');
                }
            })
            var urls = url.split("/");
            busType = urls[urls.length - 1];
            window.location.href = cIp + "/online?code=" + code + "&busType=" + busType;//cip=http://hostname:port   获取code成功后,刷新页面到指定的url  
            return;
        }
    },
    error: function () {
        MC.cookie.set("isConnection", "0");
    }
})

2.后台代码

@Autowired
private OAuthService oAuthService;

/**
 * controller
 * 用户登录状态获得和用户关联的code
 * @param request
 * @return
 */
@RequestMapping(value = "/code", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> code( HttpServletRequest request){
    String code = oAuthService.fetchOrGenCode(request);
    HashMap codeMap = new HashMap();
    codeMap.put("code",code);
    return codeMap;
}

@Override
public String fetchOrGenCode(HttpServletRequest request) {

    //获取缓存中的用户信息
    String userNo = (String) request.getSession().getAttribute("UserNo");
    if(null == userNo){
        userNo = (String) request.getSession().getAttribute("outUserNo");
    }
    return code(userNo);
}

public String code(String userNo){
    try {
        String code = getCode(userNo);
        if (null != code) {
            return code;
        }else{
            if (null != userNo) {
                String authorizationCode = GeneAuthorizationCode(userNo);
                saveCode(userNo, authorizationCode);
                addAuthCode(authorizationCode,userNo);
                return authorizationCode;
            }
        }
    } catch (Exception e) {
        if (null != userNo) {
            String authorizationCode = GeneAuthorizationCode(userNo);
            saveCode(userNo, authorizationCode);
            addAuthCode(authorizationCode,userNo);
            return authorizationCode;
        }
    }
    return null;
}

2.应用A 通过code+ak+sk值去应用B获取token值

/**
 * 过滤器  拦截/inner/* 路径下所有的请求
 */
@Configuration
@EnableAsync
@PropertySource("classpath:message.properties")
public class WebConfig {
    @Bean
    public FilterRegistrationBean innerCheckFilterRegistrationBean(InnerCheckFilter innerCheckFilter) {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(innerCheckFilter);
        filterRegistrationBean.setEnabled(true);
        filterRegistrationBean.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
        filterRegistrationBean.addUrlPatterns("/inner/*", "*.html", "*.htm");
        filterRegistrationBean.setOrder(3);
        return filterRegistrationBean;
    }
}
/**
 * 拦截器InnerCheckFilter
 */ 
@Component("innerCheckFilter")
public class InnerCheckFilter implements Filter {
    private static Set<String> beforeLoginSkipUrls = new HashSet<String>();
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("ZxtkCheck filter init...");
		beforeLoginSkipUrls.add("/inner/login");
    }
    @Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		HttpSession session = request.getSession();
		request.setCharacterEncoding("UTF-8");
		String[] tokenMessage = new String[0];
		
		String code = request.getParameter("code");
		String sessionToken = request.getParameter("token");
		if (sessionToken == null){
			if (code != null) {
				//获取taken
				OAuthAccessTokenResponse tokenResponse;
				try {
					tokenResponse = oauthClient.makeTokenRequestWithAuthCode(code);  //通过code获取token值
					if (tokenResponse != null) {//表示已经获取到了token 解析token中的值
						tokenMessage = Base64Util.deCode(tokenResponse.getAccessToken().toString()).split("#");
						session.setAttribute("token", tokenResponse.getAccessToken());
						session.setAttribute("code", code);
						session.setAttribute(code, tokenResponse.getAccessToken());
						session.setMaxInactiveInterval(tokenResponse.getExpiresIn().intValue());
					}else{
						response.sendRedirect("/login.html");
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
				response.sendRedirect("/inner/login?usrId=" + tokenMessage[1]);
			}else{
				chain.doFilter(request,response);
			}
		}else{
			chain.doFilter(request,response);
		}
	}
    @Override
    public void destroy() {
        beforeLoginSkipUrls.clear();
        log.info("InnerCheck filter destroy...");
    }
}

/**
 * 获取token的实现类
 */
@Component
public class OauthClient {
    @Value("${client.ak}")
    private String ak;

    @Value("${client.sk}")
    private String sk;

    @Value("${client.oauth.server.token.url}")
    private String OAUTH_SERVER_TOKEN_URL;

    @Value("${client.oauth.server.redirect.url}")
    private String OAUTH_SERVER_REDIRECT_URI;

    /**
     * 根据授权码获取accessToken
     * @param authCode 服务器端code
     * @return  OAuthAccessTokenResponse
     * @throws OAuthProblemException
     * @throws OAuthSystemException
     */
    public OAuthAccessTokenResponse makeTokenRequestWithAuthCode(String authCode)
            throws OAuthProblemException, OAuthSystemException {
        OAuthClientRequest request = OAuthClientRequest
                .tokenLocation(OAUTH_SERVER_TOKEN_URL)//访问地址
                .setClientId(ak)//ak
                .setClientSecret(sk)//sk
                .setGrantType(GrantType.AUTHORIZATION_CODE)
                .setCode(authCode)
                .setRedirectURI(OAUTH_SERVER_REDIRECT_URI)//返回地址
                .buildBodyMessage();

        OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
        return oAuthClient.accessToken(request);
    }
}

token返回值:

 

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半路笙歌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值