SpringBoot+Shiro+JWT简单配置要点(思路)

本文介绍了在SpringBoot中结合Shiro和JWT进行权限管理的配置要点,包括配置Security Manager、创建Realm、定制Authenticator以及自定义Filter。在配置Security Manager时,重点讨论了UsernamePasswordRealm和JWTRealm的创建。对于Authenticator,强调了异常处理的配置。在Filter部分,阐述了如何处理依赖注入问题以及如何生成和校验JWTToken。

首先, 附上一张Shiro核心架构图

 

配置要点

配置Security Manager

创建并配置Realm

在使用JWT作为凭证的web应用中, 需要有这两个Realm:

  1. UsernamePasswordRealm
  2. JWTRealm

其中UsernamePasswordRealm用于登录, JWTRealm用于对header中携带jwt的请求

配置Authenticator

在Authenticator中, 可以配置验证器的相关行为. 验证器调用Realm的doAuthentication()返回验证信息. 可以对返回的token信息(正确的验证信息)和http中的信息(用户提交的验证信息)进行校验.

Authenticator中, 一项重要配置就是: 为验证过程配置全局异常处理. 因为默认的Authenticator会将抛出的异常catch住, 因此在Realm的doAuthentication()方法中抛出的异常无法被全局异常处理程式访问到.

实现自己的AuthenticationStrategy, 并进行设置, 即可解决异常处理问题.

getAuthenticationInfo()中抛出异常, 全局异常处理却收不到自己抛出的异常

配置Filter

Shiro的验证过程通过拦截器filter实现.

在JWT场景中, 为了让Shiro对header中的jwt信息进行校验, 必须对默认的拦截器authc进行自定义.

Filter中的依赖注入

在配置的过程中, 一个坑点就是: 如果你希望将你的JWTUtil工具类(或许其它)通过依赖注入的方式注入到你的自定义filter中, 绝对不能使用Autowired注解, 因为filter的初始化早于beans的初始化, 因此是无法将bean通过autowired注入到filter类中的. 解决方法是: 通过为filter类增添构造函数, 在构造函数中传入ApplicationContext, 然后在通过context获取bean.

MyJWTUtil util;
public LoginFilter(ApplicationContext context) {
        this.util = context.getBean(MyJWTUtil.class);
    }
filterFactoryBean.getFilters().put("authc", new LoginFilter(applicationContext));

Filter的token生成

除了在controller登录时生成token外, 还可以在flter中生成token, 以供校验使用. 按道理来说, 一种Filter可以配置多种token. 但对于实际开发而言, 通常一种Filter只对应一种token. 在JWT中就是配置Filter专门用于处理JWT. 在Filter中就要生成JWTToken

Filter装配

将默认的和自带的Filter进行装配. 通常一个Filter可以配置多个url.

@Autowired
    ApplicationContext applicationContext;
    @Bean("shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager);
        HashMap<String, String> filtersMap = new LinkedHashMap<>();

        filterFactoryBean.getFilters().put("authc", new LoginFilter(applicationContext));
        filtersMap.put("/login/common", "anon");
        filtersMap.put("/**", "authc");
        filterFactoryBean.setFilterChainDefinitionMap(filtersMap);
        return filterFactoryBean;
    }


附: Filter的代码

@Data
public class LoginFilter extends AuthenticatingFilter {
    @Autowired
    public ObjectMapper mapper = new ObjectMapper();
    @Autowired
    public MyJWTUtil util;

    public LoginFilter(ApplicationContext context) {
        this.util = context.getBean(MyJWTUtil.class);
    }
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        if (!(request instanceof HttpServletRequest)){
            return null;
        }
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String jwt = httpServletRequest.getHeader("Authorization");
        JWTToken token = new JWTToken();
        token.setId(util.getClaimOfToken(jwt).getSubject());
        token.setJwt(jwt);
        return token;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        boolean res = false;
        try {
            res = executeLogin(request, response);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return res;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(mapper.writeValueAsString(ResultModel.create().setMsg("Without Logging in")));
        // 返回true表示继续处理流程, false直接退出
        return false;
    }

    @Override
    protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(mapper.writeValueAsString(ResultModel.create().setMsg("Without Logging in")));
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值