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,MyRealm中无法@Autowired注入Service的问题

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

  • 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详细总结

    一.项目整体介绍: 项目整体的结构如下图所示,项目整体采用 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自定义过滤器的方法

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

  • 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进行权限控制和管理的示例

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

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

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

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

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

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

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

  • SpringBoot集成Quartz实现定时任务的方法

    1 需求 在我的前后端分离的实验室管理项目中,有一个功能是学生状态统计.我的设计是按天统计每种状态的比例.为了便于计算,在每天0点,系统需要将学生的状态重置,并插入一条数据作为一天的开始状态.另外,考虑到学生的请假需求,请假的申请往往是提前做好,等系统时间走到实际请假时间的时候,系统要将学生的状态修改为请假. 显然,这两个子需求都可以通过定时任务实现.在网上略做搜索以后,我选择了比较流行的定时任务框架Quartz. 2 Quartz Quartz是一个定时任务框架,其他介绍网上也很详尽.这里要介

  • SpringBoot Import及自定义装配实现方法解析

    Import的注册形式: 1.使用@Import导入一个或者多个类字节对象 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { Class<?>[] value(); } 使用时一般在配置类上注解,表示该注解类导入了其他配置 @Configuration @Import({ MyBeanFactoryPostProcessor.class,

  • Springboot集成JSR303参数校验的方法实现

    JSR303 是一套 JavaBean 参数校验的标准 1.pom导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> 2.注解类型 (1)空检查 @Null 验证对象是否为null @NotNull 验证对象是否不为null,

  • Spring Boot集成Shiro并利用MongoDB做Session存储的方法详解

    前言 shiro是一个权限框架,具体的使用可以查看其官网 http://shiro.apache.org/ 它提供了很方便的权限认证和登录的功能. 而springboot作为一个开源框架,必然提供了和shiro整合的功能! 之前项目鉴权一直使用的Shiro,那是在Spring MVC里面使用的比较多,而且都是用XML来配置,用Shiro来做权限控制相对比较简单而且成熟,而且我一直都把Shiro的session放在mongodb中,这个比较符合mongodb的设计初衷,而且在分布式项目中mongo

  • Spring Boot集成Shiro实现动态加载权限的完整步骤

    一.前言 本文小编将基于 SpringBoot 集成 Shiro 实现动态uri权限,由前端vue在页面配置uri,Java后端动态刷新权限,不用重启项目,以及在页面分配给用户 角色 . 按钮 .uri 权限后,后端动态分配权限,用户无需在页面重新登录才能获取最新权限,一切权限动态加载,灵活配置 基本环境 spring-boot 2.1.7 mybatis-plus 2.1.0 mysql 5.7.24 redis 5.0.5 温馨小提示:案例demo源码附文章末尾,有需要的小伙伴们可参考哦 ~

  • SpringBoot集成SpringSecurity和JWT做登陆鉴权的实现

    废话 目前流行的前后端分离让Java程序员可以更加专注的做好后台业务逻辑的功能实现,提供如返回Json格式的数据接口就可以.SpringBoot的易用性和对其他框架的高度集成,用来快速开发一个小型应用是最佳的选择. 一套前后端分离的后台项目,刚开始就要面对的就是登陆和授权的问题.这里提供一套方案供大家参考. 主要看点: 登陆后获取token,根据token来请求资源 根据用户角色来确定对资源的访问权限 统一异常处理 返回标准的Json格式数据 正文 首先是pom文件: <dependencies

随机推荐

其他