Java Shiro apache olut 实现SSO

公司项目需求,需要做SSO统一认证,开发完毕后,把相关思路整理下。

涉及相关OAuth2基础知识:

https://www.cnblogs.com/kaleidoscope/p/9507261.html

先上流程图:

主要流程:

1.client 去认证中心获取 Auth Code

2.Client 根据 Auth Code 去资源服务器获取 Token(项目认证中心和资源服务器都统一在一个APP了)

3.Client 向认证中心请求跳转到Client 端

4.认证中心验证 Token,并向目标Client 端发送写session 的请求

5.目标Client 收到写入Session请求后,向 资源服务器验证Token,写入session,然后跳转到具体页面

 

大概流程写完了,现在上代码。

一、认证中心代码:

LoginController.java

package demo.sso.controller;

import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import demo.sso.ResponseCodes;
import demo.sso.entity.Client;
import demo.sso.entity.User;
import demo.sso.helpers.HttpRequestHelper;
import demo.sso.helpers.ShiroSecurityHelper;
import demo.sso.helpers.VerifyCodeHelper;
import demo.sso.model.ChangePasswordModel;
import demo.sso.model.JsonResult;
import demo.sso.model.LoginModel;
import demo.sso.server.ClientService;
import demo.sso.server.OAuthService;
import demo.sso.server.UserService;
import demo.sso.shiro.token.PhoneCodeToken;
import demo.sso.utils.ExceptionUtils;

@Controller
public class LoginController {
	
	@Autowired
	private HttpRequestHelper httpRequestHelper;
	
	@Autowired
	private ClientService clientService;
	
	@Autowired
	private OAuthService oAuthService;
	
	@Autowired
	private UserService userService;
	
	@Autowired
	private ShiroSecurityHelper shiroSecurityHelper;
	
	@Autowired
	private VerifyCodeHelper verifyCodeHelper;
	
	private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
	
	private List<Client> getClients()
	{
		List<Client> clients = clientService.findAll();
		List<Client> clientList = new ArrayList<Client>();;
		for(Client client :clients)
		{
			if(client.canUsed())
				clientList.add(client);
		}
		return clientList;
	}
	
	@RequestMapping(value = { "/", "/index" }, method = RequestMethod.GET)
	public Object index(HttpServletRequest request,ModelMap model)
	{
		model.addAttribute("name",shiroSecurityHelper.getName());
		model.addAttribute("clients", getClients());
		return "index";
	}
	@RequestMapping(value = { "/changePassword" }, method = RequestMethod.GET)
	public String changePassword()
	{
		return "changePassword";
	}
	@RequestMapping(value = { "/changePassword" }, method = RequestMethod.POST)
	public Object changePassword(@ModelAttribute("SpringWeb")ChangePasswordModel data 
			,ModelMap model
			,HttpServletRequest request) {
		
		if(!data.getNewPassword().equals(data.getConfirmPassword()))
		{
			ModelAndView mav=new ModelAndView("changePassword");
			mav.addObject("error", "密码不一致。");
			return mav;
		}
		if(!userService.changePassword(shiroSecurityHelper.getUserid(), data.getOldPassword(),data.getNewPassword()))
		{
			ModelAndView mav=new ModelAndView("changePassword");
			mav.addObject("error", "密码修改失败!");
			return mav;
		}
		else
		{
			return "redirect:/logout";
		}
	}
	
	@RequestMapping(value = { "/login" }, method = RequestMethod.GET)
	public Object login(HttpServletRequest req){
		if(shiroSecurityHelper.isAuthenticated())
		{
			return "redirect:/";
		}
		if(shiroSecurityHelper.isAuthenticated())
		{
	        ModelAndView mav=new ModelAndView("login");
	        mav.addObject("error", "您登录,请勿重复登录");
			return mav;
		}
		
		String error=null;
		String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
        if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
            error = "账号不正确!";
        } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
            error = "密码不正确!";
        } else if(LockedAccountException.class.getName().equals(exceptionClassName)) {
            error = "账号被锁定!";
        } else if(AuthenticationException.class.getName().equals(exceptionClassName)) {
            error = "登录失败!!";
        } else if(exceptionClassName != null) {
            error = "登录失败:" + exceptionClassName;
        }
        logger.debug(exceptionClassName);
        ModelAndView mav=new ModelAndView("login");
        mav.addObject("error", error);
		return mav;
	}
	
	@RequestMapping(value = { "/login" }, method = RequestMethod.POST)
	public Object login(@ModelAttribute("SpringWeb")LoginModel login 
			,BindingResult result
			,ModelMap model
			,HttpServletRequest request) {
		if(shiroSecurityHelper.isAuthenticated())
		{
			return "redirect:/index";
		}
		String error=null;
		try
		{
			Session session = shiroSecurityHelper.getSession();
			
			if("phone".equals(login.getType()))
			{
				PhoneCodeToken token = new PhoneCodeToken(login.getUsername(),login.getPassword());
				shiroSecurityHelper.login(token);
			}
			else
			{
				UsernamePasswordToken token=new UsernamePasswordToken(login.getUsername(),login.getPassword());
				shiroSecurityHelper.login(token);
			}
			User user = userService.findByPhone(login.getUsername());
			List navigationBar=userService.getNavigationBar(user.getUsername());
			session.setAttribute("navibar", navigationBar);
			session.setAttribute("username", user.getUsername());
			session.setAttribute("name", user.getName());
			session.setAttribute("userid", user.getUserid());
			session.setAttribute("email", user.getEmail());
			session.setAttribute("phone", user.getPhone());
		}
		catch(Exception e)
		{	
			e.printStackTrace();
			String exceptionClassName = e.getClass().getName();
	        if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
		          error = "账号不正确!";
		    } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
		          error = "密码不正确!";
		    } else if(LockedAccountException.class.getName().equals(exceptionClassName)) {
		          error = "账号被锁定!";
		    } else if(AuthenticationException.class.getName().equals(exceptionClassName)) {
		          error = "登录失败!";
		    } else if(exceptionClassName != null) {
		          error = "登录失败:" + exceptionClassName;
		    }
		    else
		    {
		    	error = "登录失败!!";
		    }
	        ModelAndView mav=new ModelAndView("login");
	        mav.addObject("error", error);
			return mav;
		}
		return "redirect:"+getReturnUrl(request,login);
	}
	@RequestMapping("/logout")
	public Object logout(HttpServletRequest request)
	{
		String accessToken = shiroSecurityHelper.getSessionAccessToken();
		
		shiroSecurityHelper.logout();
		
		if(accessToken!=null&& accessToken.length()>0)
        {
        	String username =oAuthService.getUsernameByAccessToken(accessToken.toString());
        	oAuthService.removeAccessToken(accessToken.toString());
        	
        	shiroSecurityHelper.kickOutUser(username);
        }
        System.out.println( "no token redirect:" +httpRequestHelper.getCobineUrl(request, "/login"));
        return "redirect:"+httpRequestHelper.getCobineUrl(request, "/login");
	}
	
	@RequestMapping("/checkCode")
	public Object checkCode(HttpServletRequest req,String phone){
		JsonResult result = new JsonResult();
		try
		{
			String code = verifyCodeHelper.createVerifyCode(6);
			System.out.println(code);
			boolean flag = userService.updateCode(phone, code);
			if(!flag)
			{
				result.setCode(ResponseCodes.SMS_SEND_FIAL);
			}
		}catch(Exception e)
		{
			logger.error(ExceptionUtils.getMessage(e));
			e.printStackTrace();
			result.setCode(ResponseCodes.SYSTEM_ERROR);
		}
		return new ResponseEntity(result.toString(), HttpStatus.OK);
	}
	
	private String getReturnUrl(HttpServletRequest request,LoginModel login)
	{
		String returnUrl = login.getReturnUrl();
		if(returnUrl!=null)
			return returnUrl;
		else
			return httpRequestHelper.getCobineUrl(request, "/");
	}
}

  OAuth2Controller.java

package demo.sso.controller;

import java.net.URI;
import java.net.URISyntaxException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.ResponseType;
import org.apache.oltu.oauth2.common.utils.OAuthUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.alibaba.fastjson.JSON;
import demo.sso.server.ClientService;
import demo.sso.server.OAuthService;
import demo.sso.utils.ExceptionUtils;
import demo.sso.ResponseCodes;
import demo.sso.ResponseMessages;
import demo.sso.entity.Client;
import demo.sso.entity.Status;
import demo.sso.helpers.HttpRequestHelper;
import demo.sso.helpers.ShiroSecurityHelper;
import demo.sso.mapper.ClientMapper;
import demo.sso.model.JsonResult;

@Controller
@RequestMapping("/v1/oauth2")
public class OAuth2Controller {
	@Autowired
	private ClientMapper clientMapper;
	@Autowired
	private HttpRequestHelper httpRequestHelper;
    @Autowired
    private OAuthService oAuthService;
    @Autowired
    private ClientService clientService;
    @Autowired
	private ShiroSecurityHelper shiroSecurityHelper;
    
	private static final Logger logger = LoggerFactory.getLogger(OAuth2Controller.class);
    /**
     * 服务端授权页面,获取授权 code
     * @param model
     * @param request
     * @return
     * @throws URISyntaxException
     * @throws OAuthSystemException
     */
    @SuppressWarnings("unchecked")
	@RequestMapping(value = "/authorize")
    public Object authorize(
            Model model,
            HttpServletRequest request)
            throws URISyntaxException, OAuthSystemException {
    	logger.debug("---------服务端端/authorize----------------------------------------------------------------------------------");
		
        try {

            HttpHeaders headers = new HttpHeaders();
            headers.set("Content-Type","application/json; charset=utf-8");

            //构建OAuth 授权请求
            OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);

            //检查传入的客户端id是否正确
            if (!oAuthService.checkClientId(oauthRequest.getClientId())) {
                OAuthResponse response =
                        OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                                .setErrorDescription(ResponseMessages.getMessage(ResponseCodes.INVALID_CLIENT_ID))
                                .buildJSONMessage();
                return new ResponseEntity(response.getBody(), headers, HttpStatus.valueOf(response.getResponseStatus()));
            }

            //如果用户没有登录,跳转到登陆页面
            if (!login(request)) {//登录失败时跳转到登陆页面
            	model.addAttribute("client", clientService.findByClientId(oauthRequest.getClientId()));
                return "oauth2login";
            }

            String username="";
            if ("get".equalsIgnoreCase(request.getMethod())) {
            	
            	username = shiroSecurityHelper.getUsername();
            }
            else
			{
			    username = request.getParameter("username"); //获取用户名
			}
            
            //生成授权码
            String authorizationCode = null;
            //responseType目前仅支持CODE,另外还有TOKEN
            String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
            if (responseType.equals(ResponseType.CODE.toString())) {
                OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
                authorizationCode = oauthIssuerImpl.authorizationCode();
                oAuthService.addAuthCode(authorizationCode, username);
            }

            //进行OAuth响应构建
            OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
                    OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);
            //设置授权码
            builder.setCode(authorizationCode);
            logger.debug("authorizationCode="+authorizationCode);
            
            //得到到客户端重定向地址
            String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);

            //构建响应
            final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();

            //根据OAuthResponse返回ResponseEntity响应
            headers = new HttpHeaders();
            headers.set("Content-Type","application/json; charset=utf-8");
            headers.setLocation(new URI(response.getLocationUri()));
            return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
        } catch (OAuthProblemException e) {
        	logger.error(ExceptionUtils.getMessage(e));
        	e.printStackTrace();
            //出错处理
            String redirectUri = e.getRedirectUri();
            if (OAuthUtils.isEmpty(redirectUri)) {
                //告诉客户端没有传入redirectUri直接报错
                HttpHeaders headers = new HttpHeaders();
                headers.add("Content-Type", "application/json; charset=utf-8");
                Status status = new Status();
                status.setCode(HttpStatus.NOT_FOUND.value());
                status.setMsg(ResponseMessages.getMessage(ResponseCodes.INVALID_REDIRECT_URI));
                return new ResponseEntity(JSON.toJSONString(status), headers, HttpStatus.NOT_FOUND);
            }
            //返回错误消息(如?error=)
            final OAuthResponse response =
                    OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
                            .error(e).location(redirectUri).buildQueryMessage();
            HttpHeaders headers = new HttpHeaders();
            headers.setLocation(new URI(response.getLocationUri()));
            return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
        }
    }

    /**
     * 服务端授权页面-登录验证
     * @param request
     * @return
     */
    private boolean login(HttpServletRequest request) {		
    	if ("get".equalsIgnoreCase(request.getMethod())) {
        	if(!shiroSecurityHelper.isAuthenticated())
        	{
                request.setAttribute("error", "请先登录");
                return false;
        	}    		
        	else
        	{
        		return true;
        	}
        }      
    	
    	String username = request.getParameter("username");
        String password = request.getParameter("password");

        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
              request.setAttribute("error", "登录失败:用户名或密码不能为空");
              return false;
        }
    	
    	//用户名和密码都保存在model中
        //基于shiro实现登录
        //用户名和密码保存到token中
        AuthenticationToken token= new UsernamePasswordToken(username,password);
        try {
            //如果正常登录,表示没有异常.登陆成功
        	shiroSecurityHelper.login(token);
            return true;
        } catch (Exception e) {
        	logger.error(ExceptionUtils.getMessage(e));
            //如果异常,表示登录失败,重新跳转到登录页面
            e.printStackTrace();
            request.setAttribute("error", "登录失败,服务器繁忙.");
            return false;
        }
    }

    /**
     * 根据code 获取 accessToken
     * @param request
     * @return
     * @throws URISyntaxException
     * @throws OAuthSystemException
     */
    @RequestMapping("/accessToken")
    public HttpEntity token(HttpServletRequest request)
            throws URISyntaxException, OAuthSystemException {
    	logger.debug("---------服务端端/accessToken----------------------------------------------------------------------------------");
		
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type","application/json; charset=utf-8");

        try {

            //构建OAuth请求
            OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);

            //检查提交的客户端id是否正确
            if (!oAuthService.checkClientId(oauthRequest.getClientId())) {
                OAuthResponse response =
                        OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                                .setErrorDescription(ResponseMessages.getMessage(ResponseCodes.INVALID_CLIENT_ID))
                                .buildJSONMessage();
                return new ResponseEntity(response.getBody(), headers, HttpStatus.valueOf(response.getResponseStatus()));
            }

            // 检查客户端安全KEY是否正确
            if (!oAuthService.checkClientSecret(oauthRequest.getClientSecret())) {
                OAuthResponse response =
                        OAuthASResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                                .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
                                .setErrorDescription(ResponseMessages.getMessage(ResponseCodes.INVALID_CLIENT_ID))
                                .buildJSONMessage();
                return new ResponseEntity(response.getBody(), headers, HttpStatus.valueOf(response.getResponseStatus()));
            }

            String authCode = oauthRequest.getParam(OAuth.OAUTH_CODE);
            // 检查验证类型,此处只检查AUTHORIZATION_CODE类型,其他的还有PASSWORD或REFRESH_TOKEN
            if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) {
                if (!oAuthService.checkAuthCode(authCode)) {
                    OAuthResponse response = OAuthASResponse
                            .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                            .setError(OAuthError.TokenResponse.INVALID_GRANT)
                            .setErrorDescription(ResponseMessages.getMessage(ResponseCodes.INVALID_AUTH_CODE))
                            .buildJSONMessage();
                    return new ResponseEntity(response.getBody(), headers, HttpStatus.valueOf(response.getResponseStatus()));
                }
            }

            //生成Access Token
            OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
            final String accessToken = oauthIssuerImpl.accessToken();
            oAuthService.addAccessToken(accessToken, oAuthService.getUsernameByAuthCode(authCode));

            //生成OAuth响应
            OAuthResponse response = OAuthASResponse
                    .tokenResponse(HttpServletResponse.SC_OK)
                    .setAccessToken(accessToken)
                    .setExpiresIn(String.valueOf(oAuthService.getExpireIn()))
                    .buildJSONMessage();

            oAuthService.removeAuthCode(authCode);
            
            //将最后的session信息写入到 session 中
            shiroSecurityHelper.setSessionAccessToken(accessToken);
            logger.debug("accessToken="+accessToken);
            
            //根据OAuthResponse生成ResponseEntity
            return new ResponseEntity(response.getBody(), headers, HttpStatus.valueOf(response.getResponseStatus()));
        } catch (OAuthProblemException e) {
        	logger.error(ExceptionUtils.getMessage(e));
            //构建错误响应
            OAuthResponse res = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e)
                    .buildJSONMessage();
            return new ResponseEntity(res.getBody(), headers, HttpStatus.valueOf(res.getResponseStatus()));
        }
    }

    /**
     * 验证accessToken
     *
     * @param accessToken
     * @return
     */
    @RequestMapping(value = "/checkAccessToken", method = RequestMethod.POST)
    public ResponseEntity checkAccessToken(@RequestParam("accessToken") String accessToken) {
    	logger.debug("---------服务端端/checkAccessToken----------------------------------------------------------------------------------");
		
    	boolean b = oAuthService.checkAccessToken(accessToken);
        return b ? new ResponseEntity(HttpStatus.valueOf(HttpServletResponse.SC_OK)) : new ResponseEntity(HttpStatus.valueOf(HttpServletResponse.SC_UNAUTHORIZED));
    }
    
    @RequestMapping(value = "/serverRedirect")
    public String serverRedirect(HttpServletRequest request,String accessToken,String redirect) {
    	logger.debug("---------服务端端/serverRedirect----------------------------------------------------------------------------------");
		
    	try {
			String hostStr = httpRequestHelper.getUrlContextPath(redirect);
			Client client = clientMapper.findByClientHost(hostStr);
			if(client==null)
			{
				//#TODO client 查找失败
				return "找不到客户端信息";
			}
			logger.debug("redirect:"+client.getWriteSessionUri()+"?accessToken="+accessToken+"&redirect="+redirect);
			return "redirect:"+client.getWriteSessionUri()+"?accessToken="+accessToken+"&redirect="+redirect;
		} catch (Exception e) {
			logger.error(ExceptionUtils.getMessage(e));
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		return null;
    }
    

	@RequestMapping(value = "/logout")
    public Object logout(HttpServletRequest request,@RequestParam("access_token") String accessToken)
    {
		JsonResult result = new JsonResult();	
		try	{
	        //获取用户名
	        String username = oAuthService.getUsernameByAccessToken(accessToken);
	        if(username!=null && username.length()>0)	        
	        {
	 	        oAuthService.removeAccessToken(accessToken.toString());	 	        
	 	        shiroSecurityHelper.kickOutUser(username);
	        }
		}catch(Exception e)
		{
			logger.error(ExceptionUtils.getMessage(e));
			e.printStackTrace();
			result.setCode(ResponseCodes.SYSTEM_ERROR);
		}
		return new ResponseEntity(result.toString(), HttpStatus.OK);
    }
}

  OAuth2ApiController.java

package demo.sso.controller;

import javax.servlet.http.HttpServletRequest;
import org.apache.oltu.oauth2.common.message.types.ParameterStyle;
import org.apache.oltu.oauth2.rs.request.OAuthAccessResourceRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import demo.sso.ResponseCodes;
import demo.sso.model.JsonResult;
import demo.sso.server.OAuthService;
import demo.sso.utils.ExceptionUtils;

@Controller
@RequestMapping("/v1/oauth2/api")
public class OAuth2ApiController {
	
    @Autowired
    private OAuthService oAuthService;
    
	private static final Logger logger = LoggerFactory.getLogger(OAuth2ApiController.class);
    	
	@RequestMapping(value = "/getUsername")
    public Object getUsername(HttpServletRequest request)
    {
		JsonResult result = new JsonResult();	
		try	{
			 //构建OAuth资源请求
	        OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(request, ParameterStyle.QUERY);

	        //获取Access Token
	        String accessToken = oauthRequest.getAccessToken();

	        //获取用户名
	        String username = oAuthService.getUsernameByAccessToken(accessToken);
	        if(username==null)
	        {
	        	result.setCode(ResponseCodes.INVALID_ACCESS_TOKEN);
	        	logger.debug(accessToken+" 找不到对应的用户");
	        }
	        else
	        {
	        	result.setData(username);
	        }
	        
		}catch(Exception e)
		{
			logger.error(ExceptionUtils.getMessage(e));
			result.setCode(ResponseCodes.SYSTEM_ERROR);
		}
		return new ResponseEntity(result.toString(), HttpStatus.OK);
    }
}

  

二、Client端代码

LoginController.java

package wo.hut.user.web;

import javax.naming.AuthenticationException;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import wo.hut.user.helpers.HttpRequestHelper;
import wo.hut.user.helpers.ShiroSecurityHelper;
import wo.hut.user.service.UserService;


@Controller
public class LoginController {
	@Autowired
	private HttpRequestHelper httpRequestHelper;
	@Autowired
	private UserService userService;
		@Autowired
	private ShiroSecurityHelper shiroSecurityHelper;
		
	@RequestMapping("/login")
	public Object login(HttpServletRequest req){
		if(shiroSecurityHelper.isAuthenticated())
		{
			return "redirect:/";
		}
		if(shiroSecurityHelper.isAuthenticated())
		{
	        ModelAndView mav=new ModelAndView("login");
	        mav.addObject("error", "您登录,请勿重复登录");
			return mav;
		}
		
		String error=null;
		String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
        if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
            error = "账号不正确!";
        } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
            error = "密码不正确!";
        } else if(LockedAccountException.class.getName().equals(exceptionClassName)) {
            error = "账号被锁定!";
        } else if(AuthenticationException.class.getName().equals(exceptionClassName)) {
            error = "登录失败!!";
        } else if(exceptionClassName != null) {
            error = "登录失败:" + exceptionClassName;
        }
        ModelAndView mav=new ModelAndView("login");
        mav.addObject("error", error);
		return mav;
	}
		
	@RequestMapping("/logout")
	public String logout(HttpServletRequest request,String redirect,ModelMap model)
	{
		System.out.println("---------客户端/logout----------------------------------------------------------------------------------");
		
		if(redirect==null || redirect.length()<=0)
		{			
			redirect = "/";
		}		
		
		shiroSecurityHelper.logout();
		
		return "redirect:"+redirect;
	}
}

  

OAuth2Controller.java

package wo.hut.user.web;

import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthResourceResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.ResponseType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;

import wo.hut.common.utils.ExceptionUtils;
import wo.hut.user.entity.OAuth2User;
import wo.hut.user.helpers.HttpRequestHelper;
import wo.hut.user.helpers.ShiroSecurityHelper;
import wo.hut.user.helpers.VerifyCodeHelper;
import wo.hut.user.model.JsonResult;
import wo.hut.user.service.UserService;
import wo.hut.user.shiro.token.CustomToken;

@Controller
@RequestMapping("/v1/oauth2")
public class OAuth2Controller {
	@Value("${oauth2.CLIENT_ID}")
	private String CLIENT_ID 							= "3440CE11-D067-411D-90D0-AA60B88D4A74"; // 应用id CLIENT_ID
	@Value("${oauth2.CLIENT_SECRET}")
	private String CLIENT_SECRET 						= "03D609C8-ECFC-4E4A-A0CA-87AD0CBD01A4"; // 应用secret CLIENT_SECRET
	private String OAUTH_CLIENT_SESSION_URI 			= "/v1/oauth2/writeSession"; // 客户端写入 session 地址
	private String OAUTH_CLIENT_REDIRECT_URI			= "/v1/oauth2/serverRedirectCallbackCode";//客户端跳转回调地址
	private String OAUTH_CLIENT_AUTHOIAZE_URI 			= "/v1/oauth2/authorize"; // 客户端用户授权
	@Value("${oauth2.OAUTH_SERVICE_API}")
	private String OAUTH_SERVICE_API 					= "http://localhost:8180/demo.sso/v1/oauth2/api/getUsername"; // 测试开放数据api
	@Value("${oauth2.OAUTH_SERVICE_LOGOUT}")
	private String OAUTH_SERVICE_LOGOUT 				= "http://localhost:8180/demo.sso/v1/oauth2/logout"; // 测试开放数据api
	@Value("${oauth2.OAUTH_SERVICE_REDIRECT_SESSION_URI}")
	private String OAUTH_SERVICE_REDIRECT_SESSION_URI 	= "http://localhost:8180/demo.sso/v1/oauth2/serverRedirect"; // 服务端 session 写入中转地址
	@Value("${oauth2.OAUTH_SERVER_TOKEN_URL}")
	private String OAUTH_SERVER_TOKEN_URL 				= "http://localhost:8180/demo.sso/v1/oauth2/accessToken"; // ACCESS_TOKEN获取地址
	@Value("${oauth2.OAUTH_SERVER_URL}")
	private String OAUTH_SERVER_URL 					= "http://localhost:8180/demo.sso/v1/oauth2/authorize"; // 服务端授权地址
	@Value("${oauth2.OAUTH_SERVER_CHECK_ACCESS_CODE_URL}")
	private String OAUTH_SERVER_CHECK_ACCESS_CODE_URL   = "http://localhost:8180/demo.sso/v1/oauth2/checkAccessToken";//验证token是否有效
	@Autowired
	private HttpRequestHelper httpRequestHelper;
	@Autowired 
	private VerifyCodeHelper verifyCodeHelper;
	@Autowired
	private UserService userService; 
	@Autowired
	private ShiroSecurityHelper shiroSecurityHelper;
	
	private static final Logger logger = LoggerFactory.getLogger(OAuth2Controller.class);
	
	@RequestMapping("/logout")
	public String logout(HttpServletRequest request,String redirect,ModelMap model)
	{
		logger.debug("---------客户端/logout----------------------------------------------------------------------------------");
		
		if(redirect==null || redirect.length()<=0)
		{			
			redirect = httpRequestHelper.getCobineUrl(request, "/login");
		}		
		

		String accessToken=shiroSecurityHelper.getAccessToken();
		shiroSecurityHelper.logout();
		if(accessToken==null || accessToken.length()==0)
		{
			logger.debug("没有 accessToken ,直接退出。"+redirect);
			return "redirect:"+redirect;
		}
		OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
		try {
			logger.debug(OAUTH_SERVICE_LOGOUT);
	        OAuthClientRequest userInfoRequest = new OAuthBearerClientRequest(OAUTH_SERVICE_LOGOUT)
	        .setAccessToken(accessToken).buildQueryMessage();
	        OAuthResourceResponse resourceResponse = oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);
	        String jsonStr = resourceResponse.getBody();
	        
	        JsonResult result = JSON.parseObject(jsonStr, new TypeReference<JsonResult>() {});
	        if(result==null ||!result.success())
	        {
	        	//#TODO 获取 oauth 服务器资源失败,跳转到提示页面
	        	logger.debug( jsonStr+" 向  server 退出登录失败" );
	        	model.addAttribute("message", "向  server 退出登录失败");
	        }
		}catch(Exception ex)
		{
			logger.error(ExceptionUtils.getMessage(ex));
			ex.printStackTrace();
			model.addAttribute("message", ex.toString());
		}
		return "redirect:"+redirect;
	}

	/**
	 * 客户端跳转中转地址
	 * @param request
	 * @param response
	 * @param attr
	 * @param redirect
	 * @return
	 * @throws OAuthProblemException
	 */
	@RequestMapping("/serverRedirect")
	public String serverRedirect(HttpServletRequest request, HttpServletResponse response, RedirectAttributes attr
			,String redirect)  throws OAuthProblemException
	{
		logger.debug("---------客户端/serverRedirect----------------------------------------------------------------------------------");
		//OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
		String redirectUri=httpRequestHelper.getCobineUrl(request, OAUTH_CLIENT_REDIRECT_URI+"?redirect="+redirect);
		String requestUrl = null;
		try {
			//存在 accessToken ,则直接跳转到 sso 重定向页面
			String accessToken = shiroSecurityHelper.getAccessToken();
			if(accessToken!=null)
			{
				URL checkAccessTokenUrl = new URL(OAUTH_SERVER_CHECK_ACCESS_CODE_URL+"?accessToken=" + accessToken);
				logger.debug(checkAccessTokenUrl.toString());
		        HttpURLConnection conn = (HttpURLConnection) checkAccessTokenUrl.openConnection();
		        conn.setRequestMethod("POST");
		        conn.disconnect();
		        if(HttpServletResponse.SC_OK == conn.getResponseCode())
		        {
		        	String url=OAUTH_SERVICE_REDIRECT_SESSION_URI+"?accessToken="+accessToken+"&redirect="+URLEncoder.encode( redirect);
		            logger.debug("redirect:"+url);
		            return "redirect:"+url;    
		        }
			}
			else
			{
				shiroSecurityHelper.removeAccessToken();
			}
			logger.debug(OAUTH_SERVER_URL);
			//构建oauthd的请求。设置请求服务地址(accessTokenUrl)、clientId、response_type、redirectUrl
			OAuthClientRequest accessTokenRequest = OAuthClientRequest
					.authorizationLocation(OAUTH_SERVER_URL)
			        .setResponseType(ResponseType.CODE.toString())
			        .setClientId(CLIENT_ID)
			        .setRedirectURI(redirectUri)
			        .buildQueryMessage();
			requestUrl = accessTokenRequest.getLocationUri();
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(ExceptionUtils.getMessage(e));
		}
		logger.debug("redirect:"+requestUrl );
		return "redirect:"+requestUrl ;
	}

	/**
	 * 客户端跳转回调验证地址
	 */
	@RequestMapping("/serverRedirectCallbackCode")
	public Object serverRedirectCallbackCode(HttpServletRequest request, HttpServletResponse response, RedirectAttributes attr
			,String redirect) throws OAuthProblemException
	{
		logger.debug("---------客户端/serverRedirectCallbackCode----------------------------------------------------------------------------------");
		HttpServletRequest httpRequest = (HttpServletRequest) request;
	    String code = httpRequest.getParameter("code"); 
	    System.out.println(code);
	    OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
	    try {
	    	logger.debug(OAUTH_SERVER_TOKEN_URL);
			OAuthClientRequest accessTokenRequest = OAuthClientRequest
					.tokenLocation(OAUTH_SERVER_TOKEN_URL)
			        .setGrantType(GrantType.AUTHORIZATION_CODE)
			        .setClientId(CLIENT_ID)
			        .setClientSecret(CLIENT_SECRET)
			        .setCode(code)
			        .setRedirectURI(OAUTH_SERVER_TOKEN_URL)
			        .buildQueryMessage();
			
			//去服务端请求access token,并返回响应
			OAuthAccessTokenResponse oAuthResponse = oAuthClient.accessToken(accessTokenRequest, OAuth.HttpMethod.POST);
			//获取服务端返回过来的access token 
			String accessToken = oAuthResponse.getAccessToken();
			//查看access token是否过期
            //Long expiresIn = oAuthResponse.getExpiresIn();
            shiroSecurityHelper.setAccessToken(accessToken);
            
            String url="redirect:"+OAUTH_SERVICE_REDIRECT_SESSION_URI+"?accessToken="+accessToken+"&redirect="+URLEncoder.encode( redirect);
            logger.debug(url);
            return url;            
		} catch (OAuthSystemException e) {
			e.printStackTrace();
			logger.error(ExceptionUtils.getMessage(e));
		}
	    return new ResponseEntity("sso登录失败!", HttpStatus.OK);
	}
	
	/**
	 * sso 向客户端写入session 地址
	 * @param request
	 * @param accessToken
	 * @param redirect
	 * @return
	 */
	@RequestMapping("/writeSession")
	public Object writeSession(HttpServletRequest request,HttpServletResponse response,String accessToken,String redirect) 
	{
		logger.debug("---------客户端/writeSession----------------------------------------------------------------------------------");
		
		if(shiroSecurityHelper.isAuthenticated())
		{
			shiroSecurityHelper.setAccessToken(accessToken);
	        logger.debug( "已经登录,redirect:"+redirect );
	        return "redirect:"+redirect ;
		}
		
		OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
		try {
			logger.debug(OAUTH_SERVICE_API);
	        OAuthClientRequest userInfoRequest = new OAuthBearerClientRequest(OAUTH_SERVICE_API)
	        .setAccessToken(accessToken).buildQueryMessage();
	        OAuthResourceResponse resourceResponse = oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);
	        String jsonStr = resourceResponse.getBody();
	        
	        JsonResult result = JSON.parseObject(jsonStr, new TypeReference<JsonResult>() {});
	        if(result==null || !result.success())
	        {
	        	//#TODO 获取 oauth 服务器资源失败,跳转到提示页面
	        	logger.info( jsonStr+" json 转换失败,redirect:"+redirect );
		        return "redirect:"+redirect ;
	        }
	        String ssoUsername = result.getData().toString();
	        //根据SSO 的 username 查找对应的用户信息,查询不到表示 sso 和 wo+用户未做关联
	        OAuth2User user = userService.findBySsoUsername(ssoUsername);
	        
	        
	        //创建关联关系
	        if (user==null) {	        	
	        	//#TODO 客户端检查 UserMapping 信息失败,跳转到提示页面
	        	logger.warn(ssoUsername+"---------------user mapping can not find.");
	        	String url="redirect:"+httpRequestHelper.getCobineUrl(request, OAUTH_CLIENT_AUTHOIAZE_URI)+"?accessToken="+accessToken+"&redirect="+URLEncoder.encode( redirect);
	        	logger.debug(url);
				return url;
	        }
	        
	        //写入登录session 	       
        	CustomToken token = new CustomToken(user.getUsername());
			shiroSecurityHelper.login(token);
			shiroSecurityHelper.setLoginInfo(user);
			shiroSecurityHelper.setAccessToken(accessToken);
	        
	        logger.debug( "redirect:"+redirect );
	        return "redirect:"+redirect ;

		} catch (Exception e) {
			e.printStackTrace();
			logger.error(ExceptionUtils.getMessage(e));
		}
		return new ResponseEntity("sso登录失败!", HttpStatus.OK);
	}
	/**
	   *  客户端授权用户接入到sso页面,需要验证登录
	 * @param request
	 * @param accessToken
	 * @param redirect
	 * @param model
	 * @return
	 */
	@RequestMapping("/authorize")
	public Object authorize(HttpServletRequest request,String accessToken,String redirect,ModelMap model)
	{
		logger.debug("---------客户端/authorize----------------------------------------------------------------------------------");
		
		if(!isLogin(request))
		{
			try	{
				String url=httpRequestHelper.getCobineUrl(request,"/login?returnUrl="+URLEncoder.encode(httpRequestHelper.getRequestUrl(request), "UTF-8"));
	        	return "redirect:"+url;
			}catch(Exception e)
			{
				return null;
			}
		}
		
		if ("get".equalsIgnoreCase(request.getMethod())) {
			model.addAttribute("message", "是否允许用户 "+shiroSecurityHelper.getUsername()+" 关联到【OAuth2 认证中心】?");
			return "v1/oauth2/authorize";
		}
		else
		{
			
			OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
			try {
				HttpSession session = request.getSession();
				logger.debug(OAUTH_SERVICE_API);
		        OAuthClientRequest userInfoRequest = new OAuthBearerClientRequest(OAUTH_SERVICE_API)
		        .setAccessToken(accessToken).buildQueryMessage();
		        OAuthResourceResponse resourceResponse = oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);
		        String jsonStr = resourceResponse.getBody();
		        
		        JsonResult result = JSON.parseObject(jsonStr, new TypeReference<JsonResult>() {});
		        if(result==null ||!result.success())
		        {
		        	//#TODO 获取 oauth 服务器资源失败,跳转到提示页面
		        	logger.debug( jsonStr+" 向  server 获取用户信息失败" );
		        	model.addAttribute("message", "向  server 获取用户信息失败");
		        	return "authorization/authorize";
		        }
		        String ssoUsername = result.getData().toString();
				String username = shiroSecurityHelper.getUsername();
				
				boolean flag = userService.insertMapping(username, ssoUsername);
				if(!flag)
				{
					logger.debug( jsonStr+" 插入 oauth_mapping 失败" );
					System.out.println( jsonStr+" 插入 oauth_mapping 失败" );
				}
			}catch(Exception ex)
			{
				model.addAttribute("message", ex.toString());
				return "v1/oauth2/authorize";
			}
			logger.debug("redirect:"+httpRequestHelper.getCobineUrl(request, OAUTH_CLIENT_SESSION_URI)+"?accessToken="+accessToken+"&redirect="+URLEncoder.encode( redirect));
			return "redirect:"+httpRequestHelper.getCobineUrl(request, OAUTH_CLIENT_SESSION_URI)+"?accessToken="+accessToken+"&redirect="+URLEncoder.encode( redirect);
		}
	}
	
	/**
	 * 检查是否登录
	 * @param request
	 * @return
	 */
	private boolean isLogin(HttpServletRequest request)
	{
		return shiroSecurityHelper.isAuthenticated();
	}
}

  

ClientService.java

package demo.sso.server;

import java.util.List;
import demo.sso.entity.Client;
import demo.sso.entity.User;


public interface ClientService {

    Client createClient(Client client);

    Client updateClient(Client client);

    void deleteClient(Long clientId);
    
    void logicalDeleteClient(Long clientId);

    List<Client> findAll();
    
    Client findById(Long id);
    
    Client findByClientId(String clientId);

    Client findByClientSecret(String clientSecret);
    boolean canCreateClient(Client client);
	boolean canUpdateClient(Client client);	
}

  

OAuthService.java

package demo.sso.server;

public interface OAuthService {

    //添加 auth code
    void addAuthCode(String authCode, String username);
    
    void removeAuthCode(String authCode);

    //添加 access token
    void addAccessToken(String accessToken, String username);
    
    void removeAccessToken(String accessToken);

    //验证auth code是否有效
    boolean checkAuthCode(String authCode);

    //验证access token是否有效
    boolean checkAccessToken(String accessToken);

    String getUsernameByAuthCode(String authCode);

    String getUsernameByAccessToken(String accessToken);

    //auth code / access token 过期时间
    long getExpireIn();

    boolean checkClientId(String clientId);

    boolean checkClientSecret(String clientSecret);


}

  

PermissionService.java

package demo.sso.server;

import java.util.List;
import demo.sso.entity.Permission;

public interface PermissionService {
	Long addPermission(Permission permission);
	void deletePermission(Long permissionId);
	void deleteMorePermissions(Long...permIds);
	Permission findById(Long permId);
	List<Permission> getPermissionsByRoleId(Long roleId);
	List<Permission> getAllPermissions();
	void updatePermission(Permission permission);
}

  RoleService.java

package demo.sso.server;

import java.util.List;
import demo.sso.entity.Role;

public interface RoleService {
	Long addRole(Role role,Long...permissionIds);
	void deleteRole(Long roleId);
	void deleteMoreRoles(Long...roleIds);
	Role getRoleById(Long roleId);
	List<Role> getRolesByUserName(String userName);
	List<Role> getAllRoles();
	void updateRole(Role role,Long...permIds);
	void addRolePermissions(Long roleId,Long...permissionIds);
}

  

UserService.java

package demo.sso.server;


import java.util.List;
import java.util.Set;

import demo.sso.entity.User;
import demo.sso.model.AdminUserSearchModel;
import demo.sso.model.Navigation;
import demo.sso.model.PageInfo;

public interface UserService {
	boolean canCreateUser(User user);
    User createUser(User user);
    boolean canUpdateUser(User user);
    boolean updateUser(User user);    
    void deleteUser(Long userId);
    void logicalDeleteUser(Long userId);
    void changePassword(Long userId, String newPassword);
    boolean changePassword(Long userId,String oldPassword,String newPassword);
    List<User> findAll();
    PageInfo<User> adminUserSearchList(AdminUserSearchModel search);
    User findByUsername(String username);
    User findByPhone(String phone);
    User findById(Long id);
    Set<String> findRolesByUserName(String userName);
    Set<String> findPermissionsByUserName(String userName);
    List<Navigation> getNavigationBar(String userName);
    boolean updateCode(String phone,String code);
    
}

  

具体DB层代码,就不上了。

转Java 才几月,好多地方不完善,有发现问题,请多多指教。

原文地址:https://www.cnblogs.com/hujunmin/p/10333291.html