一、背景介绍

       公司现有一个ssh框架的旧系统,想要转型使用前后端分离vue+springboot的新框架。由于一些原因,无法完全转型,所以考虑新增的业务需求,使用在旧系统中嵌套新系统页面的方式,之后新需求就可以完全使用前后端分离的新框架了。

二、解决方案

       用户登录旧系统,展示的菜单是按照新系统开发的前端工程,然后页面上所有操作都与springboot工程接口通信。springboot系统与旧ssh系统操作同一数据库。

三、登录认证

       此方案分为ssh系统、vue前端系统以及springboot系统。用户登录的是ssh旧系统,所有后面的请求需要进行登录的认证。方案是ssh系统登录后,生成token,存在localstorage中;然后展示vue前端系统时,将token传递过去;后面调用springboot接口时,将token带在请求的请求头中,这样所有请求都会带着token,即可进行登录身份认证。代码如下:   

ssh系统中使用JWT生成token:   

String token = this.getToken(userVw.getUser());
        	getRequest().setAttribute("token", token);



 /**
     * 生成token
     * @param user
     * @return
     */
    public String getToken(User user) {
    	 /**
         * token默认有效时间 1天
         */
        Long jwtTimeOut = 86400L;
    	Date date = new Date(System.currentTimeMillis() + jwtTimeOut);
        String token="";
        token= JWT.create()
        		.withAudience(user.getId().toString())// 将 user id 保存到 token 里面
//        		.withExpiresAt(date)
                .sign(Algorithm.HMAC256(user.getPasswd()));// 以 password 作为 token 的密钥
        return token;
    }

然后跳转vue前台系统时,将token传递过去,代码如下:

var token = localStorage.getItem("token");
                url=url+"&token="+token
                console.log(url);
				self.parent.parent.document.getElementById("mainFrame").src = url;

vue前端系统:

router.beforeEach((to, from, next) => {
  debugger
  var token = to.query.token;
  localStorage.setItem('logintoken', token)
}

请求的请求头中添加上token

const req = (method, url, params) => {
  return axios({
    method: method,
    url: url,
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      token: localStorage.getItem('logintoken')
    },
    data: params,
    traditional: true,
    transformRequest: [
      function (data) {
        let ret = ''
        for (let it in data) {
          ret +=
            encodeURIComponent(it) +
            '=' +
            encodeURIComponent(data[it]) +
            '&'
        }
        return ret
      }
    ]
  }).then(res => res.data);
};

springboot后台,添加依赖:

<!--jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

配置拦截器:

package com.nomen.ctms.efms.config;

import com.nomen.ctms.efms.base.interceptor.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class IntercepterConfig implements WebMvcConfigurer {
    /**
     * 对所有请求增加拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");
    }
    @Bean
    public AuthenticationInterceptor authenticationInterceptor(){
        return new AuthenticationInterceptor();
    }
}

拦截器拦截所有请求:

package com.nomen.ctms.efms.base.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.nomen.ctms.efms.base.annotation.PassToken;
import com.nomen.ctms.efms.base.annotation.NeedLoginToken;
import com.nomen.ctms.efms.system.user.entity.User;
import com.nomen.ctms.efms.system.user.service.UserService;
import com.nomen.ctms.efms.utils.CurrentUserUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        //如果不是映射到方法直接通过
        if(!(handler instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        Method method = handlerMethod.getMethod();
        //检查方法是否有通过验证的注释passtoken,有则跳过认证
        if(method.isAnnotationPresent(PassToken.class)){
            PassToken passToken = method.getAnnotation(PassToken.class);
            if(passToken.required()){
                return true;
            }
        }
        //检查有没有需要用户权限的注解
        if(method.isAnnotationPresent(NeedLoginToken.class)){
            NeedLoginToken annotation = method.getAnnotation(NeedLoginToken.class);
            if(annotation.required()){
                //执行认证
                if(token==null){
                    throw new RuntimeException("无token,请重新登录");
                }
            }
//            获取token中的userId
            String userId;
            try{
                userId = JWT.decode(token).getAudience().get(0);
            }catch (JWTDecodeException j){
                throw new RuntimeException("401");
            }
            User user = userService.getById(Long.valueOf(userId));
            CurrentUserUtil.addCurrentUser(user);

            if (user == null) {
                throw new RuntimeException("用户不存在,请重新登录");
            }
//            验证token
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(user.getPasswd())).build();
            try {
                DecodedJWT verify = verifier.verify(token);
                System.out.println(verify);
            } catch (JWTVerificationException e) {
                e.printStackTrace();
                throw new RuntimeException("401",e);
            }
            return true;
        }
        return true;
    }

}

创建注解类:

package com.nomen.ctms.efms.base.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 需要登录才能进行操作的注解
 * @Target:注解的作用目标
 * @Target(ElementType.TYPE)——接口、类、枚举、注解
 * @Target(ElementType.METHOD)——方法
 *
 * @Retention:注解的保留位置
 * RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
 *
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedLoginToken {
    boolean required() default true;
}
package com.nomen.ctms.efms.base.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 用来跳过验证的注解
 * @Target:注解的作用目标
 * @Target(ElementType.TYPE)——接口、类、枚举、注解
 * @Target(ElementType.METHOD)——方法
 *
 * @Retention:注解的保留位置
 * RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}

在需要认证的接口上添加@NeedLoginToken注解:

@NeedLoginToken
    @RequestMapping(value = "/list",method = RequestMethod.POST)
    public ResponseResult getTemplate(Page page, DocTemplate template){
        ResponseResult responseResult = new ResponseResult();
        User currentUser = CurrentUserUtil.getCurrentUser();
        System.out.println(currentUser.getUserId());

        IPage iPage = this.templateService.queryTemplate(page, template);
        List records = iPage.getRecords();
        responseResult.setData(records);
        responseResult.setSuccess(true);
        responseResult.setTotal(iPage.getTotal());
        return responseResult;
    }

四、总结

       由于本人经验也不是很充足,目前也只想到了这个方案,有什么问题或者有更好的方法,还请大佬们多多指教。