shiro 无状态登陆认证

jwt实现权限认证

pom

 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 ​
 <dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
 </dependency>
 ​
 <dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.3.2</version>
 </dependency>
 ​
 <dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.0</version>
 </dependency>
 ​
 <!--druid-->
 <dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.21</version>
 </dependency>
 <!--jwt-->
 <dependency>
  <groupId>javax.xml.bind</groupId>
  <artifactId>jaxb-api</artifactId>
 </dependency>
 <dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.0</version>
  <scope>compile</scope>
 </dependency>
 <!--json-->
 <dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.68</version>
 </dependency>

禁用session生成

 /**
  * @author spp
  * @date 2020-06-13 17:35
  **/
 public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {
  @Override
  public Subject createSubject(SubjectContext context) {
  //禁用生成session
  context.setSessionCreationEnabled(false);
  return super.createSubject(context);
  }
 }

shiro配置类

 /**
  * @author spp
  * @date 2020-06-13 17:13
  **/
 @Configuration
 public class ShiroConfig {
  //ShiroFilterFactoryBean
  @Bean
  public ShiroFilterFactoryBean getFactory(@Autowired DefaultWebSecurityManager manager){
  ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
  //设置安全管理器
 ​
  //添加shiro的内置过滤器
  /*
  anon:无需认证就可以访问
  authc:必须认证了才能访问
  user: 必须 设置 记住我才能访问
  perms: 拥有对某个资源的权限才能登陆
  role: 拥有某个角色权限才能访问
  */
  factory.setSecurityManager(manager);
  factory.getFilters().put("jwt",new JwtFilter());
  Map<String,String> filterMap = new LinkedHashMap<>();
  //filterMap.put("/login","anon");
  filterMap.put("/**","jwt");
  factory.setFilterChainDefinitionMap(filterMap);
  factory.setLoginUrl("/login");
  factory.setUnauthorizedUrl("/unauthorized");
  return factory;
  }
 ​
  @Bean
  @Autowired
  public DefaultWebSecurityManager getManager(MyRealm realm){
  DefaultWebSecurityManager manager = new DefaultWebSecurityManager();

  DefaultSessionManager defaultSessionManager = new DefaultSessionManager ();
  defaultSessionManager.setSessionValidationSchedulerEnabled(false);
  //禁用session
  manager.setSubjectFactory(new StatelessDefaultSubjectFactory());
 ​
  manager.setSessionManager(defaultSessionManager);
  // 禁用Session作为存储策略的实现。
  DefaultSubjectDAO defaultSubjectDAO = (DefaultSubjectDAO) manager.getSubjectDAO();
  DefaultSessionStorageEvaluator defaultSessionStorageEvaluatord = (DefaultSessionStorageEvaluator) defaultSubjectDAO
  .getSessionStorageEvaluator();
  defaultSessionStorageEvaluatord.setSessionStorageEnabled(false);
  manager.setRealm(realm);
  return manager;
  }
 ​
 }

自定义的jwt过滤器,继承BasicHttpAuthenticationFilter

 /**
  * @author spp
  * @date 2020-06-13 17:31
  **/
 @Slf4j
 public class JwtFilter extends BasicHttpAuthenticationFilter {
  private static final String LOGIN = "/login";
  private static final String SECRET_KEY = "auth_sp";
  @Override
  protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
  log.info("isAccessAllowed");
  HttpServletRequest r = (HttpServletRequest) request;
  if (LOGIN.equals(r.getRequestURI())){
  return true;
  }
  String token = r.getHeader(SECRET_KEY);
  try {
  Boolean expired = JwtTokenUtil.isTokenExpired(token);
  if (!expired){
  User u = JwtTokenUtil.getUsernameFromToken(token);
  log.info(u.toString() + "--->进行jwt认证");
  return true;
  }
  return false;
  } catch (Exception e) {
  request.setAttribute("error",e.getMessage());
  return false;
  }
  }

  //认证失败
  @Override
  protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
  response.setContentType("application/json;charset=utf8");
  Object error = request.getAttribute("error");
  if(!ObjectUtils.isEmpty(error)){
  response.getWriter().println(error);
  return false;
  }
  response.getWriter().println("error");
  return false;
  }
 }

jwt工具类

 /**
  * JWT生成令牌、验证令牌、获取令牌
  * @author 76986
  */
 @Component
 @NoArgsConstructor
 public class JwtTokenUtil {
 ​
  /**
  * 私钥
  */
  private static final String SECRET_KEY = "auth_sp";
 ​
  /**
  * 过期时间 毫秒,设置默认两小时过期
  */
  private static final long EXPIRATION_TIME = 3600000L * 2;
 ​
  /**
  * 生成令牌
  * @param user 用户
  * @return 令牌
  */
  public static String generateToken(User user) {
  Map<String, Object> claims = new HashMap<>(2);
  claims.put(Claims.SUBJECT,user);
  claims.put(Claims.ISSUED_AT, new Date());
  return generateToken(claims);
  }
 ​
  /**
  * 从令牌中获取用户
  * @param token 令牌
  * @return 用户名
  */
  public static User getUsernameFromToken(String token) {
  User user = new User();
  try {
  Claims claims = getClaimsFromToken(token);
  String subject = claims.getSubject();
  subject = subject.replace("{","").replace("}","");
  String[] split = subject.split(",");
  user.setUsername(split[0].split("=")[1]);
  user.setPassword(split[1].split("=")[1]);
  System.out.println(user);
  } catch (Exception e) {
  System.out.println("e = " + e.getMessage());
  }
  return user;
  }
 ​
  /**
  * 判断令牌是否过期
  *
  * @param token 令牌
  * @return 是否过期
  */
  public static Boolean isTokenExpired(String token) throws  Exception{
  try {
  Claims claims = getClaimsFromToken(token);
  Date expiration = claims.getExpiration();
  return expiration.before(new Date());
  } catch (Exception e) {
  throw new Exception("签名过期或者token不正确");
  }
  }
 ​
  /**
  * 刷新令牌
  *
  * @param token 原令牌
  * @return 新令牌
  */
  public static String refreshToken(String token) {
  String refreshedToken;
  try {
  Claims claims = getClaimsFromToken(token);
  claims.put(Claims.ISSUED_AT, new Date());
  refreshedToken = generateToken(claims);
  } catch (Exception e) {
  refreshedToken = null;
  }
  return refreshedToken;
  }
 ​
 ​
  /**
  * 从数据声明生成令牌
  *
  * @param claims 数据声明
  * @return 令牌
  */
  private static String generateToken(Map<String, Object> claims) {
  Date expirationDate = new Date(System.currentTimeMillis()+ EXPIRATION_TIME);
  HashMap<String, Object> map = new HashMap<>(1);map.put("typ",Header.JWT_TYPE);
  return Jwts.builder().setHeader(map).setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET_KEY).compact();
  }
 ​
  /**
  * 从令牌中获取数据声明
  *
  * @param token 令牌
  * @return 数据声明
  */
  private static Claims getClaimsFromToken(String token) throws Exception {
  Claims claims = null;
  try {
  claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
  } catch (Exception e) {
  new Throwable(e);
  }
  return claims;
  }
 }

登陆控制器

/**
  * @author spp
  * @date 2020-06-13 18:23
  **/
 @RestController
 public class Index {
  private Logger logger = LoggerFactory.getLogger(Index.class);
  @GetMapping("/login")
  public R login(User user){
  UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
  try {
  SecurityUtils.getSubject().login(token);
  }catch (IncorrectCredentialsException e){
  return new R(ApiErrorCode.FAILED).setMsg(e.getMessage());
  }
  String s = JwtTokenUtil.generateToken(user);
  logger.info(s);
  return new R(ApiErrorCode.SUCCESS).setMsg("登陆成功");
  }
 }

自定义登陆处理类

/**
  * @author 76986
  */
 @Component
 public class MyRealm extends AuthorizingRealm {
 ​
  /**
  * 授权
  * @param principalCollection
  * @return
  */
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  return null;
  }
 ​
  /**
  * 登陆
  * @param authenticationToken
  * @return
  * @throws AuthenticationException
  */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
  UsernamePasswordToken tok = (UsernamePasswordToken) authenticationToken;
  User user = new User("admin","123456");
  return new SimpleAuthenticationInfo(user, user.getPassword(),getName());
  }
 }

测试

先登陆拿token

携带token访问

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353