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访问

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。