Spring-Security-OAuth2遇到的奇怪问题

以SpringFramework提供的sparklr2为蓝本,照葫芦画瓢画出一个OAuth2Server程序来,运行都没有问题。当正式要做项目的时候,却一而再再二三的出现怪异的问题。

一开始,是用户登录之后,直接报空指针错误。错误原因出现在AccessConfirmationController里,这是自己写的Controller,与Spring-Security-OAuth2配搭使用的,sparklr2里面有这个Controller。主要原因是getAccessConfirmation方法的参数model,里面没有给出名为“authorizationRequest”的对象,size为0.这个参数是从session里面取出来的,这个类在sparklr(包括葫芦画瓢的测试程序中是一样的)的代码如下:


import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller for retrieving the model for and displaying the confirmation page for access to a protected resource.
 * 
 * @author Ryan Heaton
 */
@Controller
@SessionAttributes("authorizationRequest")
public class AccessConfirmationController {

	private ClientDetailsService clientDetailsService;

	@RequestMapping("/oauth/confirm_access")
	public ModelAndView getAccessConfirmation(Map<String, Object> model) throws Exception {
		AuthorizationRequest clientAuth = (AuthorizationRequest) model.remove("authorizationRequest");
		ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
		model.put("auth_request", clientAuth);
		model.put("client", client);
		return new ModelAndView("access_confirmation", model);
	}

	@RequestMapping("/oauth/error")
	public String handleError(Map<String,Object> model) throws Exception {
		// We can add more stuff to the model here for JSP rendering.  If the client was a machine then
		// the JSON will already have been rendered.
		model.put("message", "There was a problem with the OAuth2 protocol");
		return "oauth_error";
	}

	@Autowired
	public void setClientDetailsService(ClientDetailsService clientDetailsService) {
		this.clientDetailsService = clientDetailsService;
	}
}

里面声明了SessionAttributes,但方法的model里面没有。

我起初的解决方法是,将AuthorizationRequest对象添加到方法参数列表中来,用@ModelAttribute来加以修饰。果然,可以获得这个参数,也可以正常的获得accessToken了,然而通过该accessToken获取受保护的数据总是提示权限不够。

通过跟踪调查,发现Vote进行表决的时候,检查scope通不过。

客户应用提供了scope为ROLE_READ,但是提取的授权中,scope为“read+write”,本来应该为两个字符串的一个Set结果变成了只有一个字符串的Set,因此检查的时候总是通不过。

跟踪了好久,结果发现就是在AccessConfirmationController获取AuthorizationRequest对象的时候发生了问题:往session里面存的时候还是好的(AuthorizationEndpoint.authorize方法,第159行,通过往model里面put值完成设置到Session里面),scope属性为Set[read, write],但取出来就是Set[read+write]。

此时的代码如下:

import java.util.Enumeration;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller for retrieving the model for and displaying the confirmation page for access to a protected resource.
 * 
 * @author Ryan Heaton
 */
@Controller
@SessionAttributes("authorizationRequest")
public class AccessConfirmationController {
	
	private ClientDetailsService clientDetailsService;

	@RequestMapping("/oauth/confirm_access")
	public ModelAndView getAccessConfirmation(Map<String, Object> model, @ModelAttribute("authorizationRequest")AuthorizationRequest clientAuth) throws Exception {
		model.remove("authorizationRequest");
		ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
		model.put("auth_request", clientAuth);
		model.put("client", client);
		return new ModelAndView("access_confirmation", model);
	}

	@RequestMapping("/oauth/error")
	public String handleError(Map<String,Object> model) throws Exception {
		// We can add more stuff to the model here for JSP rendering.  If the client was a machine then
		// the JSON will already have been rendered.
		model.put("message", "There was a problem with the OAuth2 protocol");
		return "oauth2/error";
	}
	@Autowired
	public void setClientDetailsService(ClientDetailsService clientDetailsService) {
		this.clientDetailsService = clientDetailsService;
	}
}

后来不得已,将AuthorizationRequest对象从getAccessConfirmation的参数列表清除了出去,然后添加了HttpSession对象作为参数,然后从session中直接获取数据,结果就好了:scope=Set[read, write],这样后面的流程就都通了。

最后的代码为:

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller for retrieving the model for and displaying the confirmation page for access to a protected resource.
 * 
 * @author Ryan Heaton
 */
@Controller
@SessionAttributes("authorizationRequest")
public class AccessConfirmationController {

	private ClientDetailsService clientDetailsService;

	@RequestMapping("/oauth/confirm_access")
	public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpSession session) throws Exception {
		AuthorizationRequest clientAuth = (AuthorizationRequest) session.getAttribute("authorizationRequest");
//		 session.removeAttribute("authorizationRequest");//不要删除,删除后面会报错。model里面删除不代表从Session中删除对象
		ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
		model.put("auth_request", clientAuth);
		model.put("client", client);
		return new ModelAndView("access_confirmation", model);
	}

	@RequestMapping("/oauth/error")
	public String handleError(Map<String,Object> model) throws Exception {
		// We can add more stuff to the model here for JSP rendering.  If the client was a machine then
		// the JSON will already have been rendered.
		model.put("message", "There was a problem with the OAuth2 protocol");
		return "oauth_error";
	}

	@Autowired
	public void setClientDetailsService(ClientDetailsService clientDetailsService) {
		this.clientDetailsService = clientDetailsService;
	}
}

现在的问题是:

1.AccessConfirmationController的getAccessConfirmation为什么无法从model中获取AuthorizationRequest对象?如果将Session注入,是明明能看到里面有这个对象的。

2.将AuthorizationRequest声明在getAccessConfirmation方法中,然后用@ModelAttribute来修饰,为什么就能取出来了?但又为什么scope属性偏偏从Set[read, write]变成了Set[read+write]?谁搞的鬼?

3.在上述追踪过程中有没有遗漏什么地方,是导致这个问题的罪魁?


你可能感兴趣的:(spring,Security,oauth2)