SSM+JWT+Shiro

文章介绍了如何使用Shiro框架禁用session并实现基于JWT的无状态认证。通过自定义SubjectFactory禁止session创建,重写filter过滤器检查请求头中的token,并处理JWT的验证异常。同时,文章展示了如何重写Realm类以进行权限校验,确保用户只能访问其被授权的接口。最后,配置ShiroFilterFactoryBean来定义过滤规则和处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用户登录后生成token返回前端。前端每次请求时都在Header中添加token,后端验证token

一、禁用shiro的session


public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {
    @Override
    public Subject createSubject(SubjectContext context) {
        context.setSessionCreationEnabled(false);//关闭session创建
        return super.createSubject(context);
    }
}

二、重写filter过滤器

@Slf4j
public class AuthenFilter extends BasicHttpAuthenticationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        boolean flag = false;
        HttpServletRequest servletRequest = WebUtils.toHttp(request);
        String token = servletRequest.getHeader("token");
        if (token != null) {
            Claims claims = null;
            try {
                claims = JwtUtil.parsetJwt(token);
                this.getSubject(request, response).login(new JwtToken(token));
            } catch (Exception throwable) {
                String msg = null;
                if (throwable instanceof SignatureException) {
                    // 该异常为JWT的AccessToken认证失败(Token或者密钥不正确)
                    msg = "Token或者密钥不正确(" + throwable.getMessage() + ")";
                } else if (throwable instanceof ExpiredJwtException) {
                    msg = "Token已过期(" + throwable.getMessage() + ")";
                } else {
                    msg = throwable.getMessage();
                }
                // Token认证失败直接返回Response信息
                this.response401(response, msg);
            }
            if (claims != null && claims.getExpiration().getTime() > System.currentTimeMillis()) flag = true;
        }

        return flag;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletResponse servletResponse = WebUtils.toHttp(response);

        servletResponse.getWriter().write(JsonUtil.returnJson(4001, "未认证", null));
        return false;
    }

    /**
     * 无需转发,直接返回Response信息
     */
    private void response401(ServletResponse response, String msg) {
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setStatus(HttpStatus.OK.value());
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        try (PrintWriter out = httpServletResponse.getWriter()) {
            String data = JSON.toJSONString(new ResponseBean(HttpStatus.UNAUTHORIZED.value(), "无权访问(Unauthorized):" + msg, null));
            out.append(data);
        } catch (IOException e) {
            throw new CustomException("直接返回Response信息出现IOException异常:" + e.getMessage());
        }
    }

}

当isAccessAllowed返回为true时,直接允许访问,返回false时,shiro根据onAccessDenied的返回值决定是否允许访问

三、重写Realm类,防止用户登录后修改地址访问无权限接口

@Component
public class MyShiroRealm extends AuthorizingRealm {
    @Autowired
    @Lazy
    private IBdOrganizeEmployeeService iBdOrganizeEmployeeService;
    @Autowired
    private IBdSecuritySourcesService iBdSecuritySourcesService;


    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    private int i = 0;
    /**
     * 重写shiro权限验证,判断已分配给用户的菜单编码(现在放在ul中)和要请求的 @RequiresPermissions能否对应上,对应不上就是无权限
     * @param principalCollection
     * @return org.apache.shiro.authz.AuthorizationInfo
     * @author yueshibing
     * @data 2023/1/30 16:51
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        LoggerFactory.getLogger(MyShiroRealm.class).debug(String.valueOf(i));
        Object principal = principalCollection.getPrimaryPrincipal();
        //String userName = JwtUtil.parsetJwt(principal.toString()).get("userName").toString();
        String userId = JwtUtil.parsetJwt(principal.toString()).get("userId").toString();
        //获取菜单url
        List<BdSecuritySources> bdSecuritySourcesList = iBdSecuritySourcesService.getBdSecuritySourcesByUserID(userId);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        bdSecuritySourcesList.forEach(p -> {
            info.addStringPermission(p.getUl());
        });
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        JwtToken token = (JwtToken) authenticationToken;
        Object credentials = token.getCredentials();
        Claims claims = JwtUtil.parsetJwt(credentials.toString());
        Object userName = claims.get("userName");
        QueryWrapper<BdOrganizeEmployee> wrapper = new QueryWrapper<>();
        wrapper.select("USER_NAME");
        wrapper.eq("USER_NAME", userName);
        BdOrganizeEmployee adminuser = iBdOrganizeEmployeeService.getOne(wrapper);
        Object password = "";
        if (adminuser != null) password = token.getCredentials();
        return new SimpleAuthenticationInfo(
                token.getPrincipal(),
                password,
//                ByteSource.Util.bytes(username),
                this.getName()
        );
    }
}

判断用户菜单权限,和注解@RequiresPermissions匹配,匹配成功才可以访问,该注解用于控制器或者具体的某个方法上

四、shiro配置类

@Configuration
public class ShiroConfig {

    @Bean
    @Order(Integer.MAX_VALUE)
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //设置securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        //配置自定义过滤器
        Map<String, Filter> filterMap = new HashMap<String, Filter>();
        filterMap.put("jwtAuthen", new AuthenFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        //配置匹配规则
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login/login", "anon");
        filterChainDefinitionMap.put("/login/getLoginCode", "anon");
//        filterChainDefinitionMap.put("/login/loginOut", "anon");
        filterChainDefinitionMap.put("/**", "jwtAuthen");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    @Bean
    public SessionsSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        //配置自定义realm
        securityManager.setRealm(realm());
        //关闭session
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        sessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);

        //配置无session工厂类
        securityManager.setSubjectFactory(subjectFactory());

        return securityManager;
    }

    @Bean
    public Realm realm() {
        MyShiroRealm realm = new MyShiroRealm();
        return realm;
    }

    //配置无状态session工厂类
    public StatelessDefaultSubjectFactory subjectFactory() {
        return new StatelessDefaultSubjectFactory();
    }

    /**
     *  开启Shiro的注解(如@RequiresRoles,@RequiresPermissions)
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启aop注解支持
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SessionsSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木木的成长之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值