文章目录

平常心博客

平常心的日常积累

标签: jwt (2)

【转载】基于shiro与JWT规范实现的单点登录认证服务

在一些环境中,可能需要把Web应用做成无状态的,即服务器端无状态,就是说服务器端不会存储像会话这种东西,而是每次请求时access_token进行资源访问。如一些REST风格的API,如果不使用OAuth2协议,就可以使用如REST+JWT认证进行访问。JWT(JSON WEB Token):基于散列的消息认证码,使用一个密钥和一个消息作为输入,生成它们的消息摘要。该密钥只有服务端知道。访问时使用该消息摘要进行传播,服务端然后对该消息摘要进行验证。

认证步骤

  • 1、客户端第一次使用用户名密码访问认证服务器,服务器验证用户名和密码,认证成功,使用用户密钥生成JWT并返回
  • 2、之后每次请求客户端带上JWT
  • 3、服务器对JWT进行验证

自定义shiro拦截器 继承FormAuthenticationFilter,并改写核心认证逻辑即可

/**
* Created by LiangT on 2017/5/24.
*/
public class StatelessAuthcFilter extends FormAuthenticationFilter {
private final Logger logger = LoggerFactory.getLogger(StatelessAuthcFilter.class);
@Autowired
private SysUserMapper sysUserMapper;
/**
 * shiro权限拦截核心方法 返回true允许访问resource,这里改写了shiro源码实现,使用JWT进行认证
 *
 * author liangGTY
 * date 2017/5/25
 *
 * @param
 */
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
    //获取token
    String accessToken = getaccessToken((HttpServletRequest) request);
    if (StringUtils.isBlank(accessToken)) {
        return false;
    }
    //获取userId
    Integer userId = getUserIdForToken(accessToken);
    SysUser user = sysUserMapper.selectByPrimaryKey(userId);
    //获取用户的密钥
    String key = JWTUtil.getKey(user);
    try {
        //ExpiredJwtException JWT已过期
        //SignatureException JWT可能被篡改
        Jwts.parser().setSigningKey(key).parseClaimsJws(accessToken).getBody();
    } catch (Exception e) {
        logger.info("authentication fali  --  accessToken : {}", accessToken);
        try {
            onLoginFail(response);
        } catch (IOException e1) {
            logger.error("io exception");
        }
        return false;
    }
    // 将userId放进ThreadLocal中 方便后续业务代码获取
    RequestUtil.put(userId);
    return true;
}
/**
 * 当访问拒绝时是否已经处理了;如果返回true表示需要继续处理;如果返回false表示该拦截器实例已经处理 * 了,将直接返回即可。
 *
 * author liangGTY
 * date 2017/5/25
 *
 * @param
 */
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    //return super.onAccessDenied(request, response);
    //return true;
    if (isLoginRequest(request, response)) {
        if (isLoginSubmission(request, response)) {
            return executeLogin(request, response);
        } else {
            return true;
        }
    } else {
        //saveRequestAndRedirectToLogin(request, response);
        onLoginFail(response);
        return false;
    }
}
//鉴权失败 返回错误信息
private void onLoginFail(ServletResponse response) throws IOException {
    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    Map result = new HashMap();
    result.put(Constants.RESACCESSKEY,false);
    result.put(Constants.RESMSGKEY,Constants.RESMSGNOACCESSVALUE);
    String json = JSON.toJSONString(result);
    httpServletResponse.setHeader("Content-type", "application/json;charset=UTF-8");
    httpServletResponse.getWriter().write(json);
}
/**
 * 解码JWT 从payload中获取userId
 * @param accessToken
 * @return
 */
private Integer getUserIdForToken(String accessToken) {
    Integer userId;
    String payload = JWTUtil.parseBase64UrlEncodedPayload(accessToken);
    Map claims = JSON.parseObject(payload, Map.class);
    Object value = claims.get(Constants.PARAMUSERID);
    if (value == null) {
        return null;
    }
    if (value instanceof Integer) {
        userId = (Integer) value;
    } else if (value instanceof String) {
        userId = Integer.valueOf((String) value);
    } else {
        return null;
    }
    return userId;
}
/**
 * 从request从获取token, 先从uri参数中获取 如没有 再从cookie中获取
 * @param request
 * @return
 */
private String getaccessToken(HttpServletRequest request) {
    String accessToken = request.getParameter(Constants.PARAM_DIGEST);
    if (!StringUtils.isBlank(accessToken)) {
        return accessToken;
    }
    Cookie[] cookies = request.getCookies();
    if(null==cookies||cookies.length==0){
        return null;
    }
    for (Cookie cookie : cookies) {
        if (cookie.getName().equals(Constants.PARAM_DIGEST)) {
            accessToken = cookie.getValue();
            continue;
        }
    }
    return accessToken;
}
}

【转载】shiro jwt 构建无状态分布式鉴权体系

一:JWT

1、令牌构造

JWT(json web token)是可在网络上传输的用于声明某种主张的令牌(token),以JSON 对象为载体的轻量级开放标准(RFC 7519)。

一个JWT令牌的定义包含头信息、荷载信息、签名信息三个部分:

Header//头信息
{
    "alg": "HS256",//签名或摘要算法
    "typ": "JWT"//token类型
}
Playload//荷载信息
{
    "iss": "token-server",//签发者
    "exp ": "Mon Nov 13 15:28:41 CST 2017",//过期时间
    "sub ": "wangjie",//用户名
    "aud": "web-server-1"//接收方,
    "nbf": "Mon Nov 13 15:40:12 CST 2017",//这个时间之前token不可用
    "jat": "Mon Nov 13 15:20:41 CST 2017",//签发时间
    "jti": "0023",//令牌id标识
    "claim": {“auth”:”ROLE_ADMIN”}//访问主张
}
Signature//签名信息
签名或摘要算法(
    base64urlencode(Header),
    Base64urlencode(Playload),
    secret-key
)

按照JWT规范,对这个令牌定义进行如下操作:

base64urlencode(Header)
+"."+
base64urlencode(Playload)
+"."+
signature(
    base64urlencode(Header)
    +"."+
    base64urlencode(Playload)
    ,secret-key
)

形成一个完整的JWT:

eyJhbGciOiJIUzUxMiIsInppcCI6IkRFRiJ9.eNqqVspMLFGyMjQ1NDA1tTA0NNRRKi5NUrJSKkMS8KTFXSUUqtKEAoMDKsBQAAAP.dGLe7BVECKzQutZJqk4hbcBZthNhohuEjjue98vmpQSGn9cCYHq7lPIfwKubW8M553F8Uhk933EJwgI5vbLQ

需要注意的是:

1:荷载信息(Playload)中的属性可以根据情况进行设置,不要求必须全部填写。

2:由token的生成方式发现,Header和Playload仅仅是base64编码,通过base64解码之后可见,基本相当于是明文传输,所以应避免敏感信息放入Playload。

浏览次数:3522428   文章总数:80   评论总数:10   当前访客:8
平常心博客 © 2018 ×
¥
每天领支付宝红包
Powered by B3log 开源Solo 2.4.0