java shiro jwt,Shiro禁用Session,使用SSM+JWT+Shiro进行无状态RESTful API

应用场景:

由于项目后端管理系统不只是一个服务,而且不只在Web端运行,还会在移动端App使用,想要使用JWT方式进行无状态的RESTful API交互,也就是登录后生成token并返回给前端,前端每次请求时都在请求头里面添加token,后端验证有效性。所以Shiro自带的Session要禁用掉,同时要重新写JWT的过滤器。

使用shiro+ssm+jwt的一些前期准备:

禁用session

JWT实现工具,这个网上可以找到很多,这里就不贴代码了

自定义realm,继承AuthorizingRealm,重写认证和授权两个方法

自定义filter,继承AccessControlFilter,重写方法isAccessAllowed,onAccessDenied

一、首先,禁用shiro的session,先添加shiro的依赖到pom

org.apache.shiro

shiro-core

1.4.0

org.apache.shiro

shiro-web

1.4.0

org.apache.shiro

shiro-spring

1.4.0

org.apache.shiro

shiro-ehcache

1.4.0

二、禁用 session

package com.tg.higo.shiro;

import org.apache.shiro.subject.Subject;

import org.apache.shiro.subject.SubjectContext;

import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;

public class JwtDefaultSubjectFactory extends DefaultWebSubjectFactory {

@Override

public Subject createSubject(SubjectContext context) {

// 不创建 session

context.setSessionCreationEnabled(false);

return super.createSubject(context);

}

}

配置

applicationContext-shiro.xml

三、 重写filter 过滤器

packagecom.tg.higo.shiro;

importcom.tg.higo.common.utils.StringUtil;

importorg.apache.shiro.web.filter.AccessControlFilter;

importorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;

importorg.slf4j.Logger;

importorg.slf4j.LoggerFactory;

importjavax.servlet.ServletRequest;

importjavax.servlet.ServletResponse;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

importjava.io.IOException;

importjava.io.UnsupportedEncodingException;

importjava.net.URLEncoder;

public classJwtFilter extendsAccessControlFilter {

/*** 日志对象*/protectedLogger logger= LoggerFactory.getLogger(AdminShiroRealm.class);

/** 1. 返回true,shiro就直接允许访问url* 2. 返回false,shiro才会根据onAccessDenied的方法的返回值决定是否允许访问url* */@Overrideprotected booleanisAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throwsException {

logger.warn("isAccessAllowed 方法被调用");

//这里先让它始终返回false来使用onAccessDenied()方法return false;

}

/*** 返回结果为true表明登录通过*/@Overrideprotected booleanonAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throwsException {

logger.warn("onAccessDenied 方法被调用");

//这个地方和前端约定,要求前端将jwtToken放在请求的Header部分

//所以以后发起请求的时候就需要在Header中放一个Authorization,值就是对应的TokenHttpServletRequest request = (HttpServletRequest) servletRequest;

String jwt = getTokenFromRequest(request);

if(StringUtil.isBlank(jwt)){

onLoginFail(servletResponse);

//调用下面的方法向客户端返回错误信息return false;

}

// String jwt = request.getHeader("Authorization");logger.info("请求的 Header 中藏有 jwtToken {}", jwt);

JwtToken jwtToken = newJwtToken(jwt);

/** 下面就是固定写法* */try{

// 委托 realm 进行登录认证//所以这个地方最终还是调用JwtRealm进行的认证getSubject(servletRequest, servletResponse).login(jwtToken);

//也就是subject.login(token)} catch(Exception e) {

// e.printStackTrace();logger.info(e.getMessage());

onLoginFail(servletResponse);

//调用下面的方法向客户端返回错误信息return false;

}

return true;

//执行方法中没有抛出异常就表示登录成功}

// 从请求中获取 tokenprivateString getTokenFromRequest(ServletRequest request) {

HttpServletRequest req = (HttpServletRequest) request;

returnreq.getHeader("Token");

}

//登录失败时默认返回 401 状态码private voidonLoginFail(ServletResponse response) throwsIOException {

HttpServletResponse httpResponse = (HttpServletResponse) response;

httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

httpResponse.getWriter().write("login error");

}

}

四、重写Realm  ,定义认证,授权

package com.tg.higo.shiro;

import com.tg.higo.common.utils.JwtUtils;

import com.tg.higo.dao.UserDtoMapper;

import com.tg.higo.exception.RRException;

import com.tg.higo.model.UserDto;

import com.tg.higo.model.dto.User;

import org.apache.commons.lang.StringUtils;

import org.apache.shiro.authc.*;

import org.apache.shiro.authz.AuthorizationException;

import org.apache.shiro.authz.AuthorizationInfo;

import org.apache.shiro.authz.SimpleAuthorizationInfo;

import org.apache.shiro.realm.AuthorizingRealm;

import org.apache.shiro.subject.PrincipalCollection;

import org.apache.shiro.util.ByteSource;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import javax.annotation.Resource;

import java.util.HashSet;

import java.util.Set;

public class AdminShiroRealm extends AuthorizingRealm {

/**

* 日志对象

*/

protected Logger logger = LoggerFactory.getLogger(AdminShiroRealm.class);

/*

* 多重写一个support

* 标识这个Realm是专门用来验证JwtToken

* 不负责验证其他的token(UsernamePasswordToken)

* */

@Override

public boolean supports(AuthenticationToken token) {

//这个token就是从过滤器中传入的jwtToken

return token instanceof JwtToken;

}

//认证

//这个token就是从过滤器中传入的jwtToken

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

String jwt = (String) token.getCredentials();

// if (jwt == null) {

//

// throw new NullPointerException("jwtToken 不允许为空");

// }

//判断

// JwtUtils jwtUtil = new JwtUtils();

if (!JwtUtils.validToken(jwt)) {

throw new UnknownAccountException();

}

//下面是验证这个user是否是真实存在的

String username = (String) JwtUtils.decodeToken(jwt).getAccount();//判断数据库中username是否存在

// log.info("在使用token登录"+username);

return new SimpleAuthenticationInfo(jwt,jwt,getName());

//这里返回的是类似账号密码的东西,但是jwtToken都是jwt字符串。还需要一个该Realm的类名

}

// 授权

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

if (principalCollection == null) {

throw new AuthorizationException("PrincipalCollection method argument cannot be null.");

}

User user= JwtUtils.decodeToken(principalCollection.toString());

SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

// UserDto user = (UserDto) principalCollection.getPrimaryPrincipal();

//从数据库查询角色

Set roleSet = new HashSet<>();

roleSet.add("ghfhgfg");

authorizationInfo.setRoles(roleSet);

// //从数据库查询权限

Set permsSet = new HashSet<>();

permsSet.add("/account/update/state");

authorizationInfo.setStringPermissions(permsSet);

return authorizationInfo;

}

}

五、 shiro配置文件,applicationContext-shiro.xml

/account/login=anon

/timing/zeroTask=anon

/**=jwt,roles[ghfhgfg1]

六、总结

登录不在走认证,以后每次要授权的接口,先走认证校验是否有效,再去授权

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值