SMM+shiro集成jwt 生成token 实现与vue验证登录

之前的后台由smm+shiro搭建,最近想要pc端实现前后端分离,但移动端和管理端这些公用一个,想要实现前后端分离,而且不影响管理端的操作,管理端是由session存储用户信息,之前的pc端也是,通过shiro实现登录授权认证,现在pc端实现前后分离,这样的话前后交互不安全,同时存在跨域的问题,因此,想实现pc端前后分离,不影响 管理端,所以pc端采用jwt生成token,进行每次操作的验证。

JWT(JSON Web Tokens)是一种用于安全的传递信息而采用的一种标准。Web系统中,我们使用加密的Json来生成Token在服务端与客户端无状态传输,代替了之前常用的Session。
系统采用Redis作为缓存,解决Token过期更新的问题。

JWT登录主要流程:

  1. 登录时,密码验证通过,取当前时间戳生成签名Token,放在Response Header的Authorization属性中,同时在缓存中记录值为当前时间戳的RefreshToken,并设置有效期。
  2. 客户端请求每次携带Token进行请求。
  3. 服务端每次校验请求的Token有效后,同时比对Token中的时间戳与缓存中的RefreshToken时间戳是否一致,一致则判定Token有效。
  4. 当请求的Token被验证时抛出TokenExpiredException异常时说明Token过期,校验时间戳一致后重新生成Token并调用登录方法。
  5. 每次生成新的Token后,同时要根据新的时间戳更新缓存中的RefreshToken,以保证两者时间戳一致。

在使用jwt时  maven中添加以下


   io.jsonwebtoken
   jjwt
   0.6.0

jwt生成

/**
 * 生成token
 * @param id
 * @param subject
 * @param ttlMillis
 * @param claims
 * @return
 * @throws Exception
 */
public static  String createJWT(String id, String subject, long ttlMillis,Map claims) throws Exception {
    SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
    long nowMillis = System.currentTimeMillis();// 生成JWT的时间
    Date now = new Date(nowMillis);
    SecretKey key = generalKey();
    // 下面就是在为payload添加各种标准声明和私有声明了
    JwtBuilder builder = Jwts.builder() // 这里其实就是new一个JwtBuilder,设置jwt的body
            .setClaims(claims) // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
            .setId(id) // 设置jti(JWT
                      // ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
            .setIssuedAt(now) // iat: jwt的签发时间
            .setSubject(subject) // sub(Subject):代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
            .signWith(signatureAlgorithm, key);// 设置签名使用的签名算法和签名使用的秘钥
    if (ttlMillis >= 0) {
        long expMillis = nowMillis + ttlMillis;
        Date exp = new Date(expMillis);
        builder.setExpiration(exp); // 设置过期时间
    }
    return builder.compact(); // 就开始压缩为xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx这样的jwt
}

 

/**
 * 解密jwt
 *
 * @param jwt
 * @return
 * @throws Exception
 */
public static  Claims parseJWT(String jwt) throws Exception {
    try {
        SecretKey key = generalKey(); // 签名秘钥,和生成的签名的秘钥一模一样
        Claims claims = Jwts.parser() // 得到DefaultJwtParser
                .setSigningKey(key) // 设置签名的秘钥
                .parseClaimsJws(jwt).getBody();// 设置需要解析的jwt
        return claims;
    }catch (ExpiredJwtException e){
        return  e.getClaims();
    }
}

在applicationContext.xml中添加




   
   
      
         
      
   
   

   

   

   
      
    /public**/**=       statelessAuth
   

以上中myStatelessFilter是自己定义的,如下:

public class MyStatelessShiroFilter extends AccessControlFilter {
    private Logger logger = LoggerFactory.getLogger(MyStatelessShiroFilter.class);
    /**
     *返回false
     * @param servletRequest
     * @param servletResponse
     * @param o
     * @return 返回结果是false的时候才会执行下面的onAccessDenied方法
     * @throws Exception
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        logger.info("is access allowed");
        return false;
    }

    /**
     * 从请求头获取token并验证,验证通过后交给realm进行登录
     * @param servletRequest
     * @param servletResponse
     * @return 返回结果为true时,表明登录认证通过,执行controller层
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        logger.info("on access denied");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        if (JwtUtil.verifyToken(jwt)) {
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(jwt, jwt);
            try {
                //委托realm进行登录认证
                getSubject(servletRequest, servletResponse).login(usernamePasswordToken);
                return true;
            }catch (Exception e) {
                return false;
            }
        }
        redirectToLogin(servletRequest,servletResponse);
        return false;
    }

    /**
     * 重定向到登录页
     * @param request
     * @param response
     * @throws IOException
     */
    @Override
    protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        logger.info("redirectToLogin");
        WebUtils.issueRedirect(request, response, "/login");
    }

 

 controller中,jwt调用和token的存储

String jwt = JwtUtil.createToken(map);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(jwt, jwt);
    subject.login(usernamePasswordToken);
    Jedis jedis =SignatureUtil.pcConnectRedis();//连接redis
    String token= RSAUtils.createRSAContent(account.getSys_account_id(), RSAUtils.PUB_KEY);//加密
    jedis.set(token, jwt);
    tokeMap.put("token",token);

此处是登录成功后将生成的token存储到redis,以便以后每次访问是从redis中获取进行验证,如果token失效则让用户重新登录。

 

你可能感兴趣的:(spring,mvc,redis,token)