spring oauth2.0 demo入门分析

这里不对如何实现oauth2.0分析,也不对security做分析,读者可以google下security相关的知识,这里主要列出看oauth2.0demo时流程流转存在的疑惑。

1.oauth 2.0中的四个角色,资源拥有者,资源服务器,授权服务器,客户端。

2.spring security限制访问受限资源

3.client请求资源过程流转分析

1.oauth 2.0中的四个角色不再接受,不明白的可以google下,如果有充足的实际可以看下oauth 2.0完整译文或者原文。如果时间紧可以通过互联网对oauth2.0做初步的了解。

 

2.spring security限制资源访问

oauth2.0主要解决三方客户端访问资源需要的资源凭证的安全性,那么这里势必会牵涉到资源访问保护。spring oauth在spring security的基础上实现的,所以需要对spring security有一点的了解。
tonr spring security配置

<http access-denied-page="/login.jsp?authorization_error=true" xmlns="http://www.springframework.org/schema/security">
		<intercept-url pattern="/sparklr/**" access="ROLE_USER" />
		<intercept-url pattern="/facebook/**" access="ROLE_USER" />
		<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />

		<form-login authentication-failure-url="/login.jsp?authentication_error=true" default-target-url="/index.jsp"
			login-page="/login.jsp" login-processing-url="/login.do" />
		<logout logout-success-url="/index.jsp" logout-url="/logout.do" />
		<anonymous />
		<custom-filter ref="oauth2ClientFilter" after="EXCEPTION_TRANSLATION_FILTER" />
	</http>

上面主要表达的是对于http://localhost/sparklr/xxx这类的链接需要验证,采用的是form验证,登陆的url在form-login配置片段里给出,登陆后以客户端的身份访问资源服务器的资源。

sparklr spring security配置片段

<http pattern="/photos/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint"
		access-decision-manager-ref="accessDecisionManager" xmlns="http://www.springframework.org/schema/security">
		<anonymous enabled="false" />
		<intercept-url pattern="/photos" access="ROLE_USER,SCOPE_READ" />
		<intercept-url pattern="/photos/trusted/**" access="ROLE_CLIENT,SCOPE_TRUST" />
		<intercept-url pattern="/photos/user/**" access="ROLE_USER,SCOPE_TRUST" />
		<intercept-url pattern="/photos/**" access="ROLE_USER,SCOPE_READ" />
		<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
		<access-denied-handler ref="oauthAccessDeniedHandler" />
	</http>

上面主要是表达的是对photos资源的访问需要时授权的用户。

接下来我们来看看整个流程是怎么样的。

3.client请求资源流程

a.  用户通过浏览器访问http://localhost/tonr/,用户点击sparklr pics
b. 请求被spring给拦截,要求做登陆进行权限校验(tonr站点的验证)
c. 若登陆成功,我们点击sparklr pics访问的url是sparklr/photos所以会被tonr的SparklrController处理

@RequestMapping("/sparklr/photos")
	public String photos(Model model) throws Exception {
		model.addAttribute("photoIds", sparklrService.getSparklrPhotoIds());
		return "sparklr";
	}

上面这两句话很重要,

sparklrService.getSparklrPhotoIds()

该方法访问资源服务器的资源,资源服务器会对进行权限验证,这也是oauth协议工作的地方,我们先看是如何跳转到sparklr服务器的

public List<String> getSparklrPhotoIds() throws SparklrException {
		try {
			InputStream photosXML = new ByteArrayInputStream(
					sparklrRestTemplate.getForObject(
							URI.create(sparklrPhotoListURL), byte[].class));
			System.out.println("hiiii, i have get the photo info");

			final List<String> photoIds = new ArrayList<String>();
			SAXParserFactory parserFactory = SAXParserFactory.newInstance();
			parserFactory.setValidating(false);
			parserFactory.setXIncludeAware(false);
			parserFactory.setNamespaceAware(false);
			SAXParser parser = parserFactory.newSAXParser();
			parser.parse(photosXML, new DefaultHandler() {
				@Override
				public void startElement(String uri, String localName,
						String qName, Attributes attributes)
						throws SAXException {
					if ("photo".equals(qName)) {
						photoIds.add(attributes.getValue("id"));
					}
				}
			});
			return photoIds;
		} catch (IOException e) {
			throw new IllegalStateException(e);
		} catch (SAXException e) {
			throw new IllegalStateException(e);
		} catch (ParserConfigurationException e) {
			throw new IllegalStateException(e);
		}
	}

如果不留意可能还奇怪怎么会跳转到资源服务器进行验证了呢?

InputStream photosXML = new ByteArrayInputStream(
					sparklrRestTemplate.getForObject(
							URI.create(sparklrPhotoListURL), byte[].class));

注意到了没 URI.create(spaklrPhotoLISTURL)这里,请求资源服务器的资源,上面的配置我们看到,资源服务器会对请求校验。这里或许会有人对这个url怎么来的有些疑惑

sparklrPhotoListURL

其实在resources目录下有sparklr.properties配置文件,该文件最后被spring处理,绑定到这个ServiceBean里(spring-servlet.xml里有引用这个配置文件,看下就明白了),

<context:property-placeholder location="classpath:/sparklr.properties" />

spring会对该配置文件解析,然后将spring-servlet.xml里的占位符的地方给替换成具体的配置文件里的值。ok继续Controller往下走,最后一句不是么

return "sparklr";

先别急,资源服务器验证还没走完呢。我们明白了访问图片的时候客户端像服务器端发送http://localhost:8080/sparklr2/photos?format=xml,由于资源服务器(同时也是授权服务器)对这类请求要求权限验证,上面的配置文件说过了。所以tonr客户端会引导用户到资源服务器/授权服务器进行授权;这里表达不够清晰或者是不正确,应该是读取受限资源,tonr发起授权请求(如何处理这个异常就是spring oauth处理了)

tonr2 18:08:45.233 [DEBUG] DefaultRedirectStrategy - Redirecting to 'http://localhost:8080/sparklr2/oauth/authorize?client_id=tonr&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Ftonr%2Fsparklr%2Fphotos&response_type=code&scope=read+write&state=iVZRRl'

请求被导向了资源服务器登录页面(如果你还是不够明白,你可以想象你用新浪微博登陆某个小网站,如果你新浪微博登陆过期了, 新浪微博会要你登陆一次),登陆成功后会有一个页面让资源拥有者(用户)确认是否允许tonr访问资源,注意浏览器的url:http://localhost:8080/sparklr2/oauth/authorize?client_id=tonr&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Ftonr%2Fsparklr%2Fphotos&response_type=code&scope=read+write&state=EGwPYB(这个过程可以用chrome的开发者工具查看,结合后台log)

根据oauth2.0规范,client_id redirect_url response_type code 都是必须的,如果允许后,tonr能从授权服务器拿到一个access_token(这个过程是一个复杂的交互过程,可以写一系列的文章了,先把它当做黑盒,不影响我们分析),以后客户端拿着这个access_token去获取资源,资源服务器会检查token的有效性。到这里我们可以继续往下走了,拿到这个access_token后资源服务器会根据redirect_uri重定向到这个地址,用户在三方客户端上进行资源访问了。

我们sparklr.jsp视图(WEB-INF/jsp下),注意这个视图文件里的代码片段

<ul id="picturelist">
      <c:forEach var="sparklrPhotoId" items="${photoIds}">
        <li><img src="<c:url value="/sparklr/photos/${sparklrPhotoId}"/>"/></li>
      </c:forEach>
    </ul>
 <li><img src="<c:url value="/sparklr/photos/${sparklrPhotoId}"/>"/></li>

这里具体的图片,查看SparklrController

@RequestMapping("/sparklr/photos/{id}")
	public ResponseEntity<BufferedImage> photo(@PathVariable String id) throws Exception {
		InputStream photo = sparklrService.loadSparklrPhoto(id);
		if (photo == null) {
			throw new UnavailableException("The requested photo does not exist");
		}
		BufferedImage body;
		MediaType contentType = MediaType.IMAGE_JPEG;
		Iterator<ImageReader> imageReaders = ImageIO.getImageReadersByMIMEType(contentType.toString());
		if (imageReaders.hasNext()) {
			ImageReader imageReader = imageReaders.next();
			ImageReadParam irp = imageReader.getDefaultReadParam();
			imageReader.setInput(new MemoryCacheImageInputStream(photo), true);
			body = imageReader.read(0, irp);
		} else {
			throw new HttpMessageNotReadableException("Could not find javax.imageio.ImageReader for Content-Type ["
					+ contentType + "]");
		}
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.IMAGE_JPEG);
		return new ResponseEntity<BufferedImage>(body, headers, HttpStatus.OK);
	}

这里的分析跟获取图片所有数据类似,输入流的获取从配置文件里读取到的资源服务器的url,然后进行读

public InputStream loadSparklrPhoto(String id) throws SparklrException {
		return new ByteArrayInputStream(sparklrRestTemplate.getForObject(
				URI.create(String.format(sparklrPhotoURLPattern, id)),
				byte[].class));
	}

再然后将图片数据返回给浏览器,显示之。

这里省略了很多很多实现,只是对demo的流程进行了简易的分析。行文里如果有错误,欢迎大家批评指正。

 原文地址:http://marspring.mobi/spring-oauth2-0-demo/

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值