springboot集成shiro自定义登陆过滤器方法

目录
  • 前言
  • 自定义UsernamePasswordAuthenticationFilter
  • 覆盖默认的FormAuthenticationFilter
  • 完整UsernamePasswordAuthenticationFilter代码

前言

在上一篇博客springboot集成shiro权限管理简单实现中,用户在登录的过程中,有以下几个问题:

  • 用户在没有登陆的情况下,访问需要权限的接口,服务器自动跳转到登陆页面,前端无法控制;
  • 用户在登录成功后,服务器自动跳转到成功页,前端无法控制;
  • 用户在登录失败后,服务器自动刷新登录页面,前端无法控制;

很显然,这样的交互方式,用户体验上不是很好,并且在某些程度上也无法满足业务上的要求。所以,我们要对默认的FormAuthenticationFilter进行覆盖,实现我们自定义的Filter来解决用户交互的问题。

自定义UsernamePasswordAuthenticationFilter

首先我们需要继承原先的FormAuthenticationFilter

之所以继承这个FormAuthenticationFilter,有以下几点原因:

1.FormAuthenticationFilter是默认拦截登录功能的过滤器,我们本身就是要改造登录功能,所以继承它很正常;

2.我们自定义的Filter需要复用里面的逻辑;

public class UsernamePasswordAuthenticationFilter extends FormAuthenticationFilter{}

其次,为了解决第一个问题,我们需要重写saveRequestAndRedirectToLogin方法

/**
 * 没有登陆的情况下,访问需要权限的接口,需要引导用户登陆
 *
 * @param request
 * @param response
 * @throws IOException
 */
@Override
protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
    //  保存当前请求,以便后续登陆成功后重新请求
    this.saveRequest(request);
    // 1. 服务端直接跳转
    //   - 服务端重定向登陆页面
    if (autoRedirectToLogin) {
        this.redirectToLogin(request, response);
    } else {
        // 2. json模式
        //   - json数据格式告知前端需要跳转到登陆页面,前端根据指令跳转登陆页面
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        Map<String, String> metaInfo = new HashMap<>();
        // 告知前端需要跳转的登陆页面
        metaInfo.put("loginUrl", getLoginUrl());
        // 告知前端当前请求的url;这个信息也可以保存在前端
        metaInfo.put("currentRequest", req.getRequestURL().toString());
        ResultWrap.failure(802, "请登陆后再操作!", metaInfo)
          .writeToResponse(res);
    }
}

在这个方法中,我们通过配置autoRedirectToLogin参数的方式,既保留了原来服务器自动跳转的功能,又增强了服务器返回json给前端,让前端根据返回结果跳转到登陆页面的功能。这样就增强了应用程序的可控性和灵活性了。

重写登陆成功的处理方法onLoginSuccess:

@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
    // 查询当前用户自定义的登陆成功需要跳转的页面,可以更加灵活控制用户页面跳转
    String successUrl = loginSuccessPageFetch.successUrl(token, subject);
    // 如果没有自定义的成功页面,那么跳转默认成功页
    if (StringUtils.isEmpty(successUrl)) {
        successUrl = this.getSuccessUrl();
    }
    if (loginSuccessAutoRedirect) {
        // 服务端直接重定向到目标页面
        WebUtils.redirectToSavedRequest(request, response, successUrl);
    } else {
        SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request);
        if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase("GET")) {
            successUrl = savedRequest.getRequestUrl();
        }
        // 返回json数据格式告知前端跳转目标页面
        HttpServletResponse res = (HttpServletResponse) response;
        Map<String, String> data = new HashMap<>();
        // 登陆成功后跳转的目标页面
        data.put("successUrl", successUrl);
        ResultWrap.success(data).writeToResponse(res);
    }
    return false;
}

1.登陆成功后,我们内置了一个个性化的成功页,用于保证针对不同的用户会有定制化的登陆成功页。

2.通过自定义的loginSuccessAutoRedirect属性来决定用户登陆成功后是直接由服务端控制页面跳转还是返回json让前端控制交互行为。

3.我们在用户登陆成功后,会获取前面保存的请求,以便用户在登录成功后能直接回到登录前点击的页面。

重写用户登录失败的方法onLoginFailure:

@Override
  protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
    if (log.isDebugEnabled()) {
      log.debug("Authentication exception", e);
    }
    this.setFailureAttribute(request, e);
    if (!loginFailureAutoRedirect) {
      // 返回json数据格式告知前端跳转目标页面
      HttpServletResponse res = (HttpServletResponse) response;
      ResultWrap.failure(803, "用户名或密码错误,请核对后无误后重新提交!", null).writeToResponse(res);
    }
    return true;
  }

登陆失败我们使用自定义属性loginFailureAutoRedirect来控制失败的动作是由服务端直接跳转页面还是返回json由前端控制用户交互。

在这个方法的逻辑里面没有看到跳转的功能,是因为我们直接把父类的默认实现拿过来了,在原有的逻辑上做了修改。既然默认是服务端跳转的功能,那么我们只需要补充返回json的功能即可。

覆盖默认的FormAuthenticationFilter

现在我们已经写好了自定义的用户名密码登陆过滤器,下面我们就把它加入到shiro的配置中去,这样才能生效:

@Bean
  public ShiroFilterFactoryBean shiroFilterFactoryBean() {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager());
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    // 设置不需要权限的url
    String[] permitUrls = properties.getPermitUrls();
    if (ArrayUtils.isNotEmpty(permitUrls)) {
      for (String permitUrl : permitUrls) {
        filterChainDefinitionMap.put(permitUrl, "anon");
      }
    }
    // 设置退出的url
    String logoutUrl = properties.getLogoutUrl();
    filterChainDefinitionMap.put(logoutUrl, "logout");
    // 设置需要权限验证的url
    filterChainDefinitionMap.put("/**", "authc");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    // 设置提交登陆的url
    String loginUrl = properties.getLoginUrl();
    shiroFilterFactoryBean.setLoginUrl(loginUrl);
    // 设置登陆成功跳转的url
    String successUrl = properties.getSuccessUrl();
    shiroFilterFactoryBean.setSuccessUrl(successUrl);
    // 添加自定义Filter
    shiroFilterFactoryBean.setFilters(customFilters());
    return shiroFilterFactoryBean;
  }
​
/**
   * 自定义过滤器
   *
   * @return
   */
  private Map<String, Filter> customFilters() {
    Map<String, Filter> filters = new LinkedHashMap<>();
    // 自定义FormAuthenticationFilter,用于管理用户登陆的,包括登陆成功后的动作、登陆失败的动作
    // 可查看org.apache.shiro.web.filter.mgt.DefaultFilter,可覆盖里面对应的authc
    UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter();
    // 不允许服务器自动控制页面跳转
    usernamePasswordAuthenticationFilter.setAutoRedirectToLogin(false);
    usernamePasswordAuthenticationFilter.setLoginSuccessAutoRedirect(false);
    usernamePasswordAuthenticationFilter.setLoginFailureAutoRedirect(false);
    filters.put("authc", usernamePasswordAuthenticationFilter);
    return filters;
  }

上面的代码重点看 【添加自定义Filte】 ,其实原理就是把默认的authc过滤器给覆盖掉,换成我们自定义的过滤器,这样的话,我们的过滤器才能生效。

完整UsernamePasswordAuthenticationFilter代码

import com.example.awesomespring.vo.ResultWrap;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;
​
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
​
/**
 * @author zouwei
 * @className UsernamePasswordAuthenticationFilter
 * @date: 2022/8/2 上午12:14
 * @description:
 */
@Data
@Slf4j
public class UsernamePasswordAuthenticationFilter extends FormAuthenticationFilter {
  //  如果用户没有登陆的情况下访问需要权限的接口,服务端是否自动调整到登陆页面
  private boolean autoRedirectToLogin = true;
  // 登陆成功后是否自动跳转
  private boolean loginSuccessAutoRedirect = true;
  // 登陆失败后是否跳转
  private boolean loginFailureAutoRedirect = true;
  /**
   * 个性化定制每个登陆成功的账号跳转的url
   */
  private LoginSuccessPageFetch loginSuccessPageFetch = new LoginSuccessPageFetch(){};
​
  public UsernamePasswordAuthenticationFilter() {
  }
​
  /**
   * 没有登陆的情况下,访问需要权限的接口,需要引导用户登陆
   *
   * @param request
   * @param response
   * @throws IOException
   */
  @Override
  protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
    //  保存当前请求,以便后续登陆成功后重新请求
    this.saveRequest(request);
    // 1. 服务端直接跳转
    //   - 服务端重定向登陆页面
    if (autoRedirectToLogin) {
      this.redirectToLogin(request, response);
    } else {
      // 2. json模式
      //   - json数据格式告知前端需要跳转到登陆页面,前端根据指令跳转登陆页面
      HttpServletRequest req = (HttpServletRequest) request;
      HttpServletResponse res = (HttpServletResponse) response;
      Map<String, String> metaInfo = new HashMap<>();
      // 告知前端需要跳转的登陆页面
      metaInfo.put("loginUrl", getLoginUrl());
      // 告知前端当前请求的url;这个信息也可以保存在前端
      metaInfo.put("currentRequest", req.getRequestURL().toString());
      ResultWrap.failure(802, "请登陆后再操作!", metaInfo)
          .writeToResponse(res);
    }
  }
​
  @Override
  protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
    // 查询当前用户自定义的登陆成功需要跳转的页面,可以更加灵活控制用户页面跳转
    String successUrl = loginSuccessPageFetch.successUrl(token, subject);
    // 如果没有自定义的成功页面,那么跳转默认成功页
    if (StringUtils.isEmpty(successUrl)) {
      successUrl = this.getSuccessUrl();
    }
    if (loginSuccessAutoRedirect) {
      // 服务端直接重定向到目标页面
      WebUtils.redirectToSavedRequest(request, response, successUrl);
    } else {
      SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request);
      if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase("GET")) {
        successUrl = savedRequest.getRequestUrl();
      }
      // 返回json数据格式告知前端跳转目标页面
      HttpServletResponse res = (HttpServletResponse) response;
      Map<String, String> data = new HashMap<>();
      // 登陆成功后跳转的目标页面
      data.put("successUrl", successUrl);
      ResultWrap.success(data).writeToResponse(res);
    }
    return false;
  }
​
  @Override
  protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
    if (log.isDebugEnabled()) {
      log.debug("Authentication exception", e);
    }
    this.setFailureAttribute(request, e);
    if (!loginFailureAutoRedirect) {
      // 返回json数据格式告知前端跳转目标页面
      HttpServletResponse res = (HttpServletResponse) response;
      ResultWrap.failure(803, "用户名或密码错误,请核对后无误后重新提交!", null).writeToResponse(res);
    }
    return true;
  }
  /**
   * 针对不同的人员登陆成功后有不同的跳转页面而设计
   */
  public interface LoginSuccessPageFetch {
​
    default String successUrl(AuthenticationToken token, Subject subject) {
      return StringUtils.EMPTY;
    }
  }
}

ResultWrap.java

import com.example.awesomespring.util.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
​
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
​
/**
 * @author zouwei
 * @className ResultWrap
 * @date: 2022/8/2 下午2:02
 * @description:
 */
@Data
@AllArgsConstructor
public class ResultWrap<T, M> {
  //  方便前端判断当前请求处理结果是否正常
  private int code;
  //  业务处理结果
  private T data;
  //  产生错误的情况下,提示用户信息
  private String message;
  //  产生错误情况下的异常堆栈,提示开发人员
  private String error;
  //  发生错误的时候,返回的附加信息
  private M metaInfo;
​
  /**
   * 成功带处理结果
   *
   * @param data
   * @param <T>
   * @return
   */
  public static <T> ResultWrap success(T data) {
    return new ResultWrap(HttpStatus.OK.value(), data, StringUtils.EMPTY, StringUtils.EMPTY, null);
  }
​
  /**
   * 成功不带处理结果
   *
   * @return
   */
  public static ResultWrap success() {
    return success(HttpStatus.OK.name());
  }
​
  /**
   * 失败
   *
   * @param code
   * @param message
   * @param error
   * @return
   */
  public static <M> ResultWrap failure(int code, String message, String error, M metaInfo) {
    return new ResultWrap(code, null, message, error, metaInfo);
  }
​
  /**
   * 失败
   *
   * @param code
   * @param message
   * @param error
   * @param metaInfo
   * @param <M>
   * @return
   */
  public static <M> ResultWrap failure(int code, String message, Exception error, M metaInfo) {
    return failure(code, message, error.getStackTrace().toString(), metaInfo);
  }
​
  /**
   * 失败
   *
   * @param code
   * @param message
   * @param error
   * @return
   */
  public static ResultWrap failure(int code, String message, Exception error) {
    String errorMessage = StringUtils.EMPTY;
    if (Objects.nonNull(error)) {
      errorMessage = error.getStackTrace().toString();
    }
    return failure(code, message, errorMessage, null);
  }
​
  /**
   * 失败
   *
   * @param code
   * @param message
   * @param metaInfo
   * @param <M>
   * @return
   */
  public static <M> ResultWrap failure(int code, String message, M metaInfo) {
    return failure(code, message, StringUtils.EMPTY, metaInfo);
  }
​
  private static final String APPLICATION_JSON_VALUE = "application/json;charset=UTF-8";
​
  /**
   * 把结果写入响应中
   *
   * @param response
   */
  public void writeToResponse(HttpServletResponse response) {
    int code = this.getCode();
    if (Objects.isNull(HttpStatus.resolve(code))) {
      response.setStatus(HttpStatus.OK.value());
    } else {
      response.setStatus(code);
    }
    response.setContentType(APPLICATION_JSON_VALUE);
    try (PrintWriter writer = response.getWriter()) {
      writer.write(JsonUtil.obj2String(this));
      writer.flush();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

JsonUtil.java

​import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
​
import java.util.Objects;
​
/**
 * @author zouwei
 * @className JsonUtil
 * @date: 2022/8/2 下午3:08
 * @description:
 */
@Slf4j
public final class JsonUtil {
​
  /** 防止使用者直接new JsonUtil() */
  private JsonUtil() {}
​
  private static ObjectMapper objectMapper = new ObjectMapper();
​
  static {
    // 对象所有字段全部列入序列化
    objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
    /** 所有日期全部格式化成时间戳 因为即使指定了DateFormat,也不一定能满足所有的格式化情况,所以统一为时间戳,让使用者按需转换 */
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
    /** 忽略空Bean转json的错误 假设只是new方式创建对象,并且没有对里面的属性赋值,也要保证序列化的时候不报错 */
    objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    /** 忽略反序列化中json字符串中存在,但java对象中不存在的字段 */
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  }
​
  /**
   * 对象转换成json字符串
   *
   * @param obj
   * @param <T>
   * @return
   */
  public static <T> String obj2String(T obj) {
    return obj2String(obj, null);
  }
  /**
   * 对象转换成json字符串
   *
   * @param obj
   * @param <T>
   * @return
   */
  public static <T> String obj2String(T obj, String defaultValue) {
    if (Objects.isNull(obj)) {
      return defaultValue;
    }
    try {
      return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
    } catch (Exception e) {
      log.warn("Parse object to String error", e);
      // 即使序列化出错,也要保证程序走下去
      return null;
    }
  }
​
  /**
   * 对象转json字符串(带美化效果)
   *
   * @param obj
   * @param <T>
   * @return
   */
  public static <T> String obj2StringPretty(T obj) {
    if (Objects.isNull(obj)) {
      return null;
    }
    try {
      return obj instanceof String
          ? (String) obj
          : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
    } catch (Exception e) {
      log.warn("Parse object to String error", e);
      // 即使序列化出错,也要保证程序走下去
      return null;
    }
  }
​
  /**
   * json字符串转简单对象
   *
   * @param <T>
   * @param json
   * @param clazz
   * @return
   */
  public static <T> T string2Obj(String json, Class<T> clazz) {
    if (StringUtils.isEmpty(json) || Objects.isNull(clazz)) {
      return null;
    }
    try {
      return clazz.equals(String.class) ? (T) json : objectMapper.readValue(json, clazz);
    } catch (Exception e) {
      log.warn("Parse String to Object error", e);
      // 即使序列化出错,也要保证程序走下去
      return null;
    }
  }
​
  /**
   * json字符串转复杂对象
   *
   * @param json
   * @param typeReference 例如:new TypeReference<List<User>>(){}
   * @param <T> 例如:List<User>
   * @return
   */
  public static <T> T string2Obj(String json, TypeReference<T> typeReference) {
    if (StringUtils.isEmpty(json) || Objects.isNull(typeReference)) {
      return null;
    }
    try {
      return (T)
          (typeReference.getType().equals(String.class)
              ? (T) json
              : objectMapper.readValue(json, typeReference));
    } catch (Exception e) {
      log.warn("Parse String to Object error", e);
      // 即使序列化出错,也要保证程序走下去
      return null;
    }
  }
​
  /**
   * json字符串转复杂对象
   *
   * @param json
   * @param collectionClass 例如:List.class
   * @param elementClasses 例如:User.class
   * @param <T> 例如:List<User>
   * @return
   */
  public static <T> T string2Obj(
      String json, Class<?> collectionClass, Class<?>... elementClasses) {
    if (StringUtils.isEmpty(json)
        || Objects.isNull(collectionClass)
        || Objects.isNull(elementClasses)) {
      return null;
    }
    JavaType javaType =
        objectMapper
            .getTypeFactory()
            .constructParametricType(collectionClass, elementClasses);
    try {
      return objectMapper.readValue(json, javaType);
    } catch (Exception e) {
      log.warn("Parse String to Object error", e);
      // 即使序列化出错,也要保证程序走下去
      return null;
    }
  }
}

这样在shiro中如何实现更灵活的登陆控制就编写完毕了。后面会陆续讲解我在使用shiro时遇到的其他问题,以及相应的解决方案。

到此这篇关于springboot集成shiro自定义登陆过滤器方法的文章就介绍到这了,更多相关springboot集成shiro 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springboot集成shiro详细总结

    一.项目整体介绍: 项目整体的结构如下图所示,项目整体采用 springboot + mybatis + jsp + mysql 来完成的,下面会详细介绍下: 二.数据库脚本 先在数据库中创建 user.role.permission 这三张表,table.sql 的内容如下所示: DROP DATABASE IF EXISTS shiro_test; CREATE DATABASE shiro_test; USE shiro_test; SET NAMES utf8mb4; SET FOREI

  • springboot整合shiro与自定义过滤器的全过程

    目录 filter自定义过滤器  增加了 对验证码的校验 Shiro中的权限控制 总结 filter自定义过滤器  增加了 对验证码的校验 package com.youxiong.filter; import com.youxiong.shiro.UsernamePasswordKaptchaToken; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.subject.Subject; imp

  • springboot集成shiro遭遇自定义filter异常的解决

    目录 springboot集成shiro遭遇自定义filter异常 1.用maven添加shiro 2.配置shiro 3.实现自定义的Realm.filter.SubjectFactory等 4.重点记录filter配置中出现的问题 5.解决方案 shiro自定义异常无效 springboot集成shiro遭遇自定义filter异常 首先简述springboot使用maven集成shiro 1.用maven添加shiro <!--shiro--> <dependency> <

  • springboot集成shiro权限管理简单实现

    目录 前言 依赖 配置 Filter过滤器配置 securityManager配置 Realm配置 密码加密 测试 前言 为了解决项目当中的权限管理问题,我们一般会选择引入spring security或者shiro框架来帮助我们更好地更快地构建权限管理体系. 依赖 首先第一步,我们需要给当前项目引入对应的依赖包.与spring boot集成一般首选starter包. <!-- shiro权限管理框架 --> <dependency>    <groupId>org.a

  • SpringBoot集成Shiro进行权限控制和管理的示例

    shiro apache shiro 是一个轻量级的身份验证与授权框架,与spring security 相比较,简单易用,灵活性高,springboot本身是提供了对security的支持,毕竟是自家的东西.springboot暂时没有集成shiro,这得自己配. 1 . 添加依赖 <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId

  • SpringBoot集成shiro,MyRealm中无法@Autowired注入Service的问题

    网上说了很多诸如是Spring加载顺序,shiroFilter在Spring自动装配bean之前的问题,其实也有可能忽略如下低级错误. 在ShiroConfiguration中要使用@Bean在ApplicationContext注入MyRealm,不能直接new对象. 道理和Controller中调用Service一样,都要是SpringBean,不能自己new. 错误方式: @Bean(name = "securityManager") public SecurityManager

  • springboot自定义过滤器的方法

    过滤器是Servlet的规范,是基于函数回调的,需要实现javax.servlet.Filter接口,依赖于Tomcat等容器,一般用于过滤请求的URL. 1自定义过滤器 自定义filter的实现,本质上只有一种方式,就是实现Filter接口.但是在spring中我们有时候也会通过继承框架提供的XXXFilter,例如OncePerRequestFilter. AbstractAuthenticationProcessingFilter(Spring Security使用的认证过滤器),当然,这

  • springboot集成shiro自定义登陆过滤器方法

    目录 前言 自定义UsernamePasswordAuthenticationFilter 覆盖默认的FormAuthenticationFilter 完整UsernamePasswordAuthenticationFilter代码 前言 在上一篇博客springboot集成shiro权限管理简单实现中,用户在登录的过程中,有以下几个问题: 用户在没有登陆的情况下,访问需要权限的接口,服务器自动跳转到登陆页面,前端无法控制: 用户在登录成功后,服务器自动跳转到成功页,前端无法控制: 用户在登录失

  • SpringBoot集成JWT实现登陆验证的方法详解

    1:首先,我们需要在项目中导入两个依赖: <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.3</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifa

  • spring boot 集成 shiro 自定义密码验证 自定义freemarker标签根据权限渲染不同页面(推荐

    项目里一直用的是 spring-security ,不得不说,spring-security 真是东西太多了,学习难度太大(可能我比较菜),这篇博客来总结一下折腾shiro的成果,分享给大家,强烈推荐shiro,真心简单 : ) 引入依赖 <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4

  • SpringBoot集成QQ第三方登陆的实现

    一.什么是第三方登录,第三方登陆平台如何选择 1.简介 第三方登录不得不说的一个优势:那就是本地注册和第三方注册的选择.虽然这是一个不能称之为问题的问题,其中的心理学不得不说:做为一个app的新用户,你有两个选择:1.做一个很受欢迎的用户,注册帐号,完善信息,over.2.选用第三方登录/注册,简单方便.二者供你选择,随心挑选. 2.普通注册分多种情况: 1.传统的注册方式,注册门槛较高,易在注册页面流失用户. 2.第三方登录后,随机分配一个id 3.邮件注册,id手动填写并且不能重复 App开

  • AngularJS2 与 D3.js集成实现自定义可视化的方法

    本文介绍了ANGULAR2 与 D3.js集成实现自定义可视化的方法,分享给大家,具体如下: 目标 展现层与逻辑层分离 数据与可视化组件相分离 数据与视图双向绑定,实时更新 代码结构清晰,易于维护与修改 基本原理 angular2 的组件生命周期钩子方法\父子组件交互机制\模板语法 源码解析 代码结构很简单,其中除主页index.html和main.ts之外的代码结构如下所示: 代码结构 app.module.ts import { BrowserModule } from '@angular/

  • springboot集成与使用Sentinel的方法

    前言 在上一篇中,我们初步了解了Sentinel的基本概念,以及其有关限流方面的基础理论,本篇将通过简单的与框架进行整合,看看Sentinel如何在实际项目中进行使用 控制台安装与部署 在实际的小微服务中,使用Sentinel做限流还有另一个强大的利器,就是其提供的dashboard,尽管我们可以通过编写Sentinel提供的一些API限流规则封装一些通用的方法,但是这对于很多初次接触Sentinel的同学来说,学习成本仍然不小,而提供的dashboard可以很方便的通过界面配置的方式达到上一篇

  • SpringBoot集成slf4j日志配置的方法

    目录 前言 1.slf4j概述 2.pom.xml的日志依赖 3.application.yml的日志配置 4.logback.xml配置文件定义 5.logback.xml配置文件解析 5.1 定义日志的存储路径 5.2 定义日志的输出格式 5.3 定义控制台输出 5.4 定义日志相关参数 5.5 定义日志的输出级别 6.测试日志输出 7.小结 前言 日志,作为一个应用系统的重要部分,然而并不会在项目预研或需求设计阶段就作为一个功能模块单独规划设计,但这丝毫不影响它在任何一个系统中的重要的地位

随机推荐