api接口的实现
来源:互联网 发布:监管网络的部门 编辑:程序博客网 时间:2024/05/06 16:45
我们目前有一个系统,使用spring data jpa访问数据库,使用spring mvc提供rest接口给网站系统,同时使用shiro提供权限控制,目前需要对外部系统提供接口,需要满足以下情况:
* 若目前已经存在了这样的接口, 不再另外提供,同时权限部分得满足;
* 提供外部系统的接口权限使用token实现,即外部系统需要先获取到token,然后将token设置到cookie上,系统再根据token获取用户信息判断权限,token在一定时间内有效。
* 提供给外部系统的不能访问原系统的接口(不共用的接口);
实现思路
首先,每一个@RequestMapping可以配置多个不同的请求路径,对于外部接口提供的接口,添加API后缀即可区分,添加拦截器实现对后缀是API接口的拦截(获取token,并自动登陆系统),在完成请求时通过封装将结果同一格式返回到前台,用户登录时,将token和用户对于的user信息保存到缓存中。
实现
- 多个用户登录踢出及session过期处理 拦截器
/** * @类名 KickoutSessionControlFilter * @描述 多个用户登录踢出及session过期处理 拦截器 * @作者 zhuxl * @创建日期 2016年12月30日 下午2:47:33 */public class KickoutSessionControlFilter implements Filter { private Logger logger=Logger.getLogger(KickoutSessionControlFilter.class); private UserSessionRedisService userSessionRedisService; private UserService userService; public void destroy() { } public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request=(HttpServletRequest)req; String url=request.getRequestURI(); if(url.matches("^.{0,}/api/\\w{1,}/\\w{1,}API.{0,}$")){//是外部接口访问,从cookie中获取token并登陆 Cookie[] cookies=request.getCookies(); if(cookies!=null&&cookies.length>0){ for(Cookie cookie:cookies){ if("token".equalsIgnoreCase(cookie.getName())){ String token=cookie.getValue(); String userId=userSessionRedisService.get(SessionType.API, token, -1); User user=userService.findById(userId); if(user!=null){ Subject curUser=SecurityUtils.getSubject(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken( user.getId(), user.getPassword()); try{ curUser.login(usernamePasswordToken); }catch(Exception e){ e.printStackTrace(); } request.getSession(true).setAttribute("user", user); } filterChain.doFilter(req, res); return; } } } filterChain.doFilter(req, res); return; } //web、微信登录 用户未登录,通过,后面使用shiro进行权限(角色)拦截 Subject subject = SecurityUtils.getSubject(); HttpServletResponse response = (HttpServletResponse) res; if (!subject.isAuthenticated() && !subject.isRemembered()) { filterChain.doFilter(req, res); return; } Session session = subject.getSession(false); if(session==null){ filterChain.doFilter(req, res); return; } User user=(User)session.getAttribute("user"); SessionType type=(SessionType)session.getAttribute("loginType"); if(user==null||type==null){ filterChain.doFilter(req, res); return; } //获取用户session String sessionId=null; if(type==SessionType.WECHAT){ sessionId=userSessionRedisService.get(type,user.getOpenId(), -1); }else if(type==SessionType.WEB){ sessionId=userSessionRedisService.get(type,user.getId(),1800000); } if(StrUtil.isBlank(sessionId)){ //用户session已经失效 logger.info("登录失效了!"); try{ subject.logout(); }catch(Exception ex){ } response.setStatus(510); Map<String, Object> maps=new HashMap<String, Object>(); maps.put("code", ErrorCode.NoLogin); maps.put("msg", "用户登录已失效!!"); response.getOutputStream().write(ReturnFormat.retParam(maps).getBytes()); return; }else if(session.getId().equals(sessionId)){ //用户已经登录,并且是同一个session,继续 logger.info("用户已登录系统!!"); filterChain.doFilter(req, res); return; }else if(type==SessionType.WECHAT){ //用户已经在其他地方登录了,对于微信,直接挤上去 try{ subject.logout(); }catch(Exception ex){ ex.printStackTrace(); } logger.info("用户已经在其他地方登录了,登录类型为微信,将直接挤上去"); User newUser=userService.findByOpenId(user.getOpenId()); if(newUser!=null){//存在该openId Subject curUser=SecurityUtils.getSubject(); Session newSession=curUser.getSession(true); newSession.setAttribute("user", newUser); newSession.setAttribute("loginType", SessionType.WECHAT); newSession.setTimeout(1000*60*60*24); userSessionRedisService.add(type,newUser.getOpenId(), newSession.getId().toString(),-1); UsernamePasswordToken token = new UsernamePasswordToken( newUser.getOpenId(), newUser.getPassword()); try{ curUser.login(token); }catch(Exception e){ e.printStackTrace(); } filterChain.doFilter(req, res); }else{//不存在openID response.setStatus(510); Map<String, Object> maps=new HashMap<String, Object>(); maps.put("code", ErrorCode.NoLogin); maps.put("msg", "用户未绑定账号!!"); response.getOutputStream().write(ReturnFormat.retParam(maps).getBytes()); } return ; }else if(type==SessionType.WEB){ //用户已经在其他地方登录了,对于web,直接退出 logger.info("用户已经在其他地方登录了,登录类型为:web,直接退出"); try{ subject.logout(); }catch(Exception ex){ ex.printStackTrace(); } response.setStatus(510); Map<String, Object> maps=new HashMap<String, Object>(); maps.put("code", ErrorCode.NoLogin); maps.put("msg", "用户已经在其他地方登录了"); response.getOutputStream().write(ReturnFormat.retParam(maps).getBytes()); return ; } } //初始化 public void init(FilterConfig config) throws ServletException { WebApplicationContext wac =WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext()); userSessionRedisService=(UserSessionRedisService)wac.getBean("userSessionRedisServiceImpl"); userService=(UserService)wac.getBean("userServiceImpl"); }}
- 错误编码
/** * @类名 ErrorCode * @描述 错误编码 * @作者 zhuxl * @创建日期 2016年12月30日 下午4:28:38 */public enum ErrorCode { /** * ErrorCode NoLogin 未登录系统,需要退出 */ NoLogin, /** * ErrorCode Error 普通错误,具体错看msg */ Error, /** * ErrorCode NoPermission 无权限访问 */ NoPermission, /** * ErrorCode LossParam 缺少参数 */ LossParam, /** * ErrorCode HasEntity 已经存在(唯一性存在) */ HasEntity, /** * ErrorCode NoEntity 没有对应的实体 */ NoEntity, /** * ErrorCode HasLogin 已经登录了系统 */ HasLogin, /** * ErrorCode OK 成功 */ OK}
- Api调用返回结果
/** * @类名 ApiMsg * @描述 Api调用返回 * @作者 zhuxl * @创建时间 2017-4-19下午02:21:33 */public class ApiMsg implements Serializable{ /** * long serialVersionUID */ private static final long serialVersionUID = -5590788323134429419L; /** * int code 编码 -1 未登录,1 成功,0 其他错误 */ private ErrorCode code; /** * String msg 信息 */ private String msg; /** * Object result 返回结果 */ private Object result; public ErrorCode getCode() { return code; } public void setCode(ErrorCode code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getResult() { return result; } public void setResult(Object result) { this.result = result; } public ApiMsg(ErrorCode code, String msg, Object result) { super(); this.code = code; this.msg = msg; this.result = result; } public ApiMsg(Object result) { this.code=ErrorCode.OK; this.msg="成功"; this.result = result; }}
- 对于外部系统调用,将统一给出ApiMsg作为返回结果,拦截返回结果@ResponseBody
/** * @类名 ApiResponseBodyAdvice * @描述 对于外部系统调用,将统一给出ApiMsg作为返回结果 * @作者 zhuxl * @创建时间 2017-5-26下午05:18:39 */@ControllerAdvicepublic class ApiResponseBodyAdvice implements ResponseBodyAdvice<Object>{ @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if(body instanceof ApiMsg){ return body; }else{ String url=request.getURI().getPath(); if(url.matches("^.{0,}/api/\\w{1,}/\\w{1,}API.{0,}$")){ //对string类型做特殊处理 if(body instanceof String&& StringHttpMessageConverter.class.equals(selectedConverterType)){ return JSONObject.toJSONString(new ApiMsg(body)); }else{ return new ApiMsg(body); } }else{ return body; } } }}
- 全局异常处理,对于前台调用返回510错误,外部接口调用还是返回200正常,由code代表是否正常,msg具体错误原因
/** * @类名 GlobalExceptionHandler * @描述 全局异常处理 * @作者 zhuxl * @创建时间 2017-5-26下午02:25:15 */@ControllerAdvicepublic class GlobalExceptionHandler { private Logger logger=Logger.getLogger(GlobalExceptionHandler.class); /** * handleException * @描述: 处理PhyException异常 * @作者: zhuxl * @创建时间: 2016-7-14下午07:45:16 * @param request * @param response * @param phyException * @return */ @ExceptionHandler(PhyException.class) public @ResponseBody ApiMsg handleException(PhyException phyException,HttpServletRequest request,HttpServletResponse response){ String url=request.getRequestURI(); if(url.matches("^.{0,}/api/\\w{1,}/\\w{1,}API.{0,}$")){ return new ApiMsg(phyException.getCode(),phyException.getCode()==ErrorCode.NoLogin?"ssid失效或者未登录":phyException.getMessage(),null); }else{ response.setStatus(510); return new ApiMsg(phyException.getCode(),phyException.getMessage(),null); } } /** * 用户未登录系统 * @return */ @ExceptionHandler(UnauthenticatedException.class) public @ResponseBody ApiMsg handleUnauthenticatedException(HttpServletRequest request,HttpServletResponse response){ String url=request.getRequestURI(); if(url.matches("^.{0,}/api/\\w{1,}/\\w{1,}API.{0,}$")){ return new ApiMsg(ErrorCode.NoLogin,"token失效或者未登录",null); }else{ Subject subject=SecurityUtils.getSubject(); if(subject==null||!subject.isAuthenticated()){ response.setStatus(510); return new ApiMsg(ErrorCode.NoLogin,"用户未登录系统",null); }else{ response.setStatus(510); return new ApiMsg(ErrorCode.NoLogin,"用户已经登录系统",null); } } } /**用户无权限访问 * @return */ @ExceptionHandler(AuthorizationException.class) public @ResponseBody ApiMsg handleAuthorizationException(HttpServletRequest request,HttpServletResponse response){ String url=request.getRequestURI(); if(url.matches("^.{0,}/api/\\w{1,}/\\w{1,}API.{0,}$")){ return new ApiMsg(ErrorCode.NoPermission,"无权限访问",null); }else{ response.setStatus(510); return new ApiMsg(ErrorCode.NoPermission,"无权限访问",null); } } /** * handleOtherException * @描述 其他错误返回 * @作者 zhuxl * @创建时间 2017年2月20日 下午5:45:50 * @return */ @ExceptionHandler(value={Exception.class,Error.class}) public @ResponseBody ApiMsg handleOtherException(Throwable throwable,HttpServletRequest request,HttpServletResponse response){ String url=request.getRequestURI(); logger.error("系统出错",throwable); if(url.matches("^.{0,}/api/\\w{1,}/\\w{1,}API.{0,}$")){ return new ApiMsg(ErrorCode.Error,"系统出错了",null); }else{ response.setStatus(510); return new ApiMsg(ErrorCode.Error,"系统出错了",null); } }}
- API返回结果之前,自动退出,使得外部接口调用无session信息
/** * @类名 ApiInterceptor * @描述 对API调用的接口进行拦截,退出其登录状态(前台将无JSESSIONID) * @作者 zhuxl * @创建时间 2017-4-21上午10:40:37 */public class ApiInterceptor extends HandlerInterceptorAdapter{ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { String url=request.getRequestURI(); if(url.matches("^.{0,}/api/\\w{1,}/\\w{1,}API.{0,}$")){ org.apache.shiro.subject.Subject curUser=SecurityUtils.getSubject(); if(curUser.isAuthenticated()||curUser.isRemembered()){ try{ curUser.logout(); }catch (Exception e) { e.printStackTrace(); } } /*HttpSession session=request.getSession(false); if(session!=null){ session.invalidate(); }*/ } }}
- 外部接口登录
/** * loginApi * @描述 外部接口登录,将返回一个token,token(32位)一天有效,过期后将无法使用,需要重新获取,重复调用将重复生成 * @作者 zhuxl * @创建时间 2017-4-19下午03:21:45 * @param nameAndPwd * @return */ @RequestMapping(value = {"loginAPI"}, method = RequestMethod.POST) public String loginApi(@Valid @RequestBody LoginNameAndPwd nameAndPwd,BindingResult result){ if(result.hasFieldErrors()){ throw new PhyException(ErrorCode.Error,result.getFieldError().getDefaultMessage()); } User user2 = userService.findByLoginName(nameAndPwd.getLoginName()); if (user2 == null) { throw new PhyException(ErrorCode.Error,"当前登录用户不存在"); } if (user2.getPassword().equals(CryptUtil.encrypt(nameAndPwd.getTextPwd(), user2.getSalt()))) { String token=UUID.randomUUID().toString().replace("-", ""); userSessionRedisService.add(SessionType.API,token, user2.getId(),86400000L); logger.info(new LogModel(true, "user api login system success,loginName:"+user2.getLoginName())); return token; } else { throw new PhyException(ErrorCode.Error,"登录名或者密码不正确"); } }
- 对于俩种都需要的,添加一个请求url即可。
/** * findMyStation * @描述 查找我的监测站(包括状态) * @作者 zhuxl * @创建时间 2017年2月6日 下午1:51:04 * @param session * @return */ @RequiresUser @RequestMapping(value={"findMyStation","findMyStationAPI"},method=RequestMethod.GET) public List<StationMapTag> findMyStation(HttpSession session){ User user=(User)session.getAttribute("user"); if(user==null){ throw new PhyException(ErrorCode.NoLogin,"未登录系统"); } Role role=user.getRole(); List<Station> stations=new ArrayList<Station>(); List<StationMapTag> stationTags=new ArrayList<StationMapTag>(); if(role.getRoleName().equals("admin")){ stations=stationService.findAll(); }else{ stations=stationService.findByArea(user.getArea().getId()); } //此处省略 }
- spring mvc配置如下:
<context:component-scan base-package="com.phy.smartcity.eps.*.openapi,com.phy.smartcity.eps.filter"> </context:component-scan> <!-- Json Converter --> <bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> </bean> <!-- <bean id="stringMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8"/> </bean> --> <!-- validator --> <mvc:interceptors> <bean class="com.phy.smartcity.eps.filter.ApiInterceptor"></bean> </mvc:interceptors> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <!-- 如果不加默认到 使用classpath下的 ValidationMessages.properties --> <property name="validationMessageSource" ref="messageSource" /> </bean> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames"> <list> <value>classpath:ValidationMessages_zh_CN</value> <value>classpath:org/hibernate/validator/ValidationMessages_zh_CN</value> </list> </property> <property name="useCodeAsDefaultMessage" value="false" /> <property name="defaultEncoding" value="UTF-8" /> <property name="cacheSeconds" value="60" /> </bean> <!-- Handler Adapter --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <!-- <ref bean="stringMessageConverter" /> --> <ref bean="jacksonMessageConverter" /> </list> </property> </bean> <!-- file uploader support --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8" /> <property name="maxUploadSize" value="10485760" /> </bean> <mvc:annotation-driven validator="validator" /> <aop:config proxy-target-class="true"></aop:config> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean>
阅读全文
0 0
- api接口的实现
- Python实现简单的API接口
- Win32 API实现CDC类的FillSolidRect接口
- 根据中国气象局提供的API接口实现天气查询
- 根据中国气象局提供的API接口实现天气查询
- hadoop新版的api接口实现启动运行hadoop代码
- PHP应用API接口实现
- tslib 的 API 接口
- seajs 的api接口
- MySQL的API接口
- usrp的API接口
- API的接口变迁
- api接口的数字签名
- API接口开发 配置、实现、测试 Yii2 基于RESTful架构的 advanced版API接口开发 配置、实现、测试
- 通过HOOK系统的API接口实现对API功能的修改
- API接口与webservice接口的区别
- API接口函数的应用
- WINHTTP的API接口说明。
- CentOS6.5 mini 安装和基本配置
- 如何破解运动世界校园模拟器检测
- Combination Sum
- 垃圾回收机制详解
- Nginx的安装
- api接口的实现
- disable hwui---hardware render
- 计算机编码详解
- 自定义控件之柱状图
- 51nod-1119 机器人走方格(组合数学,离散数学)
- Combination Sum (without Duplication)
- Django 配置 Ueditor
- 设计模式-观察者模式 C++实现
- 实现queue和stack的拷贝构造函数的一个思路