带你深入使用shiro,自定义token过滤器

依赖

shiro自定义请求头token,实现权限过滤
废话不多说,直接上代码,如果不了解shiro的基本使用,建议先入门shiro

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.5.3</version>
</dependency>

大概结构
在这里插入图片描述

ShiroAuthFilter 自定义过滤器

自定义过滤器,extends AuthenticatingFilter 重写过滤token方法

package com.spshiro.auth;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author 落叶飘零
 * @version 1.0
 * @description: 会在ShiroBeanConfig的配置类里装载该 token过滤器
 * @date 2022/5/19 1:39 下午
 */
public class ShiroAuthFilter extends AuthenticatingFilter {
    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        String token = getToken((HttpServletRequest) servletRequest);
        if(StringUtils.hasText(token)){
            //ShiroAuthToken是自己定义实现AuthenticationToken接口的token
           return new ShiroAuthToken(token);
        }
        //executeLogin调用该方法token==null就抛异常,我们在onAccessDenied入口
        //重写方法没有token就提前返回并携带原因
        return null;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        return false;
    }

    /**
     * 登录失败,重写方法返回失败原因
     * @param token
     * @param e
     * @param request
     * @param response
     * @return false
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        //重写登录失败,把登录失败的原因返回给前端
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Access-Control-Allow-Origin", ((HttpServletRequest)request).getHeader("Origin"));
        try {
            //处理登录失败的异常
            Throwable throwable = e.getCause() == null ? e : e.getCause();
            //返回401 new JsonResult.error(throwable.getMessage())
            httpResponse.getWriter().print("401");
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return false;
    }

    /**
     * 程序入口 检验token是否携带,没携带就返回提示没有token
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        //获取用户token
        String token = getToken((HttpServletRequest) servletRequest);
        //如果token不存在提前返回401
        if(!StringUtils.hasText(token)){
            HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
            httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpResponse.setHeader("Access-Control-Allow-Origin", ((HttpServletRequest)servletRequest).getHeader("Origin"));
            //new JsonResult.error(invalid token)
            httpResponse.getWriter().print("401");
            return false;
        }
        //executeLogin方法会校验createToken是否返回了非null AuthenticationToken
        //上面我们提前校验提前返回信息给前端
        return executeLogin(servletRequest, servletResponse);
    }

    /**
     * 方法获取用户的token
     * @param request
     * @return token
     */
    public String getToken(HttpServletRequest request){
        //获取请求头
        String token = request.getHeader("token");
        //没有请求头就从参数里拿
        if (!StringUtils.hasText(token)){
            token=request.getParameter("token");
        }
        return token;
    }
}

ShiroAuthRealm

package com.spshiro.auth;

import com.spshiro.entity.SysUserEntity;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Set;

/**
 * @author 落叶飘零
 * @version 1.0
 * @description: TODO
 * @date 2022/5/19 1:29 下午
 */
@Component
public class ShiroAuthRealm extends AuthorizingRealm {
    /**
     * 判断是否支持token的类型 ****important****
     * 每一个Ream都有一个supports方法,用于检测是否支持此Token,默认的采用了return false
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof ShiroAuthToken;
    }
    /**
     * 授权 集合会与 @RequiresPermissions()声明的权限方法匹配,通常不调用权限的方法不会执行
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SysUserEntity user = (SysUserEntity) principalCollection.getPrimaryPrincipal();
        System.out.println("授权方法检索权限:当前的用户="+user);
        //权限集合
        Set<String> permsSet=new HashSet<>();
        //根据用户的信息查权限存入set
        if("123".equals(user.getUserId())){
            permsSet.add("sys:need");
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permsSet);
        return info;
    }
    /**
     * 认证,登录的时候会调用
     * 调用时机
     * Subject subject = SecurityUtils.getSubject();
     * subject.login(token);
     * 在自定义token过滤器的executeLogin方法也会调用到上面两行,所以也会执行到下面的doGetAuthenticationInfo,每次请求都会认证
     * authenticationToken能强转成字符串,因为用的是自定义的String类型的token
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String token = (String) authenticationToken.getPrincipal();
        //通过token到redis缓存里获取到对应用户user
        SysUserEntity userEntity=new SysUserEntity();
        if(token.equals("123")){
            userEntity.setUsername("落叶飘零");
            userEntity.setUserId("123");
            userEntity.setPassword("123");
            System.out.println("认证token");
        }else{
            throw new IncorrectCredentialsException("token失效");
        }
        //user为空 token失效
        //throw new IncorrectCredentialsException("token失效,请重新登录");
        //也可以增加ip校验
        //HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        //            String ip = IPUtils.getIpAddr(request);
        //            if (!StringUtils.equals(ip, user.getLoginIp())) {
        //                throw new IncorrectCredentialsException("非法使用token");
        //            }
        //是否锁定
        
        //userEntity参数会让 SysUserEntity user = (SysUserEntity) principalCollection.getPrimaryPrincipal();强转
        //同时可以做到程序全局能直接获取当前用户的信息
        //Subject subject = SecurityUtils.getSubject();
        // Object principal = subject.getPrincipal();
        //(SysUserEntity) principal;
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userEntity, token, getName());
        return info;
    }
}

ShiroAuthToken 自定义token

自定义token

package com.spshiro.auth;

import org.apache.shiro.authc.AuthenticationToken;

/**
 * @author 落叶飘零
 * @version 1.0
 * @description: 自定义token,在自定义token过滤器里使用
 * @date 2022/5/19 1:47 下午
 */
public class ShiroAuthToken implements AuthenticationToken {

    private String token;
    public ShiroAuthToken(String token){
        this.token=token;
    }
    @Override
    public String getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}

ShiroBeanConfig

工厂及SecurityManager

package com.spshiro.config;

import com.spshiro.auth.ShiroAuthFilter;
import com.spshiro.auth.ShiroAuthRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author 落叶飘零
 * @version 1.0
 * @description: TODO
 * @date 2022/5/19 1:25 下午
 */
@Configuration
public class ShiroBeanConfig {

    @Bean("securityManager")
    public SecurityManager securityManager(ShiroAuthRealm shiroAuthRealm){
    //ShiroAuthRealm是自定义reaml
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        manager.setRealm(shiroAuthRealm);
        return manager;
    }
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilter=new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        //装载自定义的token过滤器
        Map<String, Filter> tokenFilterMap=new HashMap<>();
        tokenFilterMap.put("oauth2",new ShiroAuthFilter());
        shiroFilter.setFilters(tokenFilterMap);
        //普通路径过滤规则
        Map<String, String> filterMap = new LinkedHashMap<>();
        //登录
        filterMap.put("/sys/login", "anon");
        //验证码
        filterMap.put("/captcha.jpg", "anon");
        // 注册用户
        filterMap.put("/sys/user/reg", "anon");
        //swagger
        filterMap.put("/swagger-resources/**", "anon");
        filterMap.put("/swagger/**", "anon");
        filterMap.put("/v2/api-docs", "anon");
        filterMap.put("/v2/api-docs-ext", "anon");
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/doc.html", "anon");
        filterMap.put("/druid/**", "anon");
        // api接口放权
        filterMap.put("/api/**", "anon");
        // websocket 放权
        filterMap.put("/ws/**", "anon");
        //微信登录放权
        filterMap.put("/auth/login", "anon");
        filterMap.put("/auth/code", "anon");
        //微信绑定用户放权
        filterMap.put("/auth/bind", "anon");
        //统一认证登录放权
        filterMap.put("/auth/cas", "anon");
        //注册、分享、邀请链接放权
        filterMap.put("/register", "anon");
        filterMap.put("/share", "anon");
        filterMap.put("/invite", "anon");
        //图片预览放权(主要用在内容中的图片)
        filterMap.put("/file/image/**", "anon");
        // 静态资源
        filterMap.put("/static/**", "anon");
        // 接收第三方系统消息放权
        filterMap.put("/sys/message/push","anon");

        //自定义的token过滤器拦截其他任何请求
        filterMap.put("/**", "oauth2");
        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;
    }
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

}

UserUtil 用户获取工具方法

全局获取当前user信息

package com.spshiro.util;

import com.spshiro.entity.SysUserEntity;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;

/**
 * @author 落叶飘零
 * (SysUserEntity) principal在realm认证传入SysUserEntity类型数据可强转
 * @version 1.0
 * @description: TODO
 * @date 2022/5/19 7:47 下午
 */
public class UserUtil {
    public static SysUserEntity getCurrentUser() {
        Subject subject = SecurityUtils.getSubject();
        if (subject == null) {
            return null;
        }
        Object principal = subject.getPrincipal();
        if (principal == null) {
            return null;
        }
        return (SysUserEntity) principal;
    }
}

loginController

package com.spshiro.controller;

import com.spshiro.entity.SysUserEntity;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DelegatingSubject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 落叶飘零
 * @version 1.0
 * @description: TODO
 * @date 2022/5/19 7:52 下午
 */
@RestController
public class LoginController {
    /**
     * 登录不登录 ****不重要*****,只要缓存里有对应的token就行
     * @param username
     * @param password
     * @return
     */
    @GetMapping("/sys/login")
    public String login(String username,String password){
//        SysUserEntity userEntity=new SysUserEntity();
//        userEntity.setUsername(username);
//        userEntity.setPassword(password);
        //自己判断收否登录成功
        //是否禁用
        //....
//        if(成功){
//            String token =创建token;
//            //存入redis等缓存
//            Cookie tokenCookie = new Cookie("token", token);
//            tokenCookie.setPath("/");
//            tokenCookie.setHttpOnly(false);
//            response.addCookie(tokenCookie);
//            return JsonResult(token,过期时间);
//        }
        return "error";
    }

    /**
     * 第三方登录,
     * @param username
     * @param password
     * @return
     */
    @GetMapping("/home")
    public String loginCas(String username,String password){
        //从cas拿到用户信息
//        与上面同理创建token
        return "ok";
    }
}

测试controller

package com.spshiro.controller;

import com.spshiro.entity.SysUserEntity;
import com.spshiro.service.TestServiceImpl;
import com.spshiro.util.UserUtil;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 落叶飘零
 * @version 1.0
 * @description: TODO
 * @date 2022/5/19 3:33 下午
 */
@RestController
public class TestRestController {
    @Autowired
    private TestServiceImpl testService;
    @GetMapping("/share")
    public String share(){
        return "share";
    }
    @GetMapping("/needToken")
    public String needToken(){
        SysUserEntity currentUser = UserUtil.getCurrentUser();
        System.out.println("needToken访问的用户="+currentUser);
        testService.needPermission();
        return "成功访问"+currentUser;
    }

}
@Service
public class TestServiceImpl {
//标注权限,需要在realm授权集合里才能访问
    @RequiresPermissions(value = {"sys:need"})
    public String needPermission(){
        return "permission ok";
    }
}

测试

http://localhost:8080/needToken?token=123
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Spring Boot中使用Shiro进行自定义过滤器的配置,可以按照以下步骤进行操作: 1. 在pom.xml文件中添加Shiro的依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency> ``` 2. 创建自定义过滤器类UserAuthenticatingFilter和UserAuthenticatingOrJwtTokenFilter,继承自Shiro提供的AuthenticatingFilter类。 UserAuthenticatingFilter类用于实现基于表单认证的过滤器,该过滤器可以在用户访问需要身份认证的资源时进行身份认证,并将用户的登录信息保存到Shiro的Subject对象中。 UserAuthenticatingOrJwtTokenFilter类用于实现基于JWT令牌认证的过滤器,该过滤器可以在用户访问需要身份认证的资源时进行身份认证,如果请求头中携了有效的JWT令牌,则使用JWT令牌进行身份认证,否则使用基于表单的身份认证方式。 3. 在Shiro配置类中进行过滤器的配置: ``` @Configuration public class ShiroConfig { // ... @Bean public UserAuthenticatingFilter userAuthenticatingFilter() { return new UserAuthenticatingFilter(); } @Bean public UserAuthenticatingOrJwtTokenFilter userAuthenticatingOrJwtTokenFilter() { return new UserAuthenticatingOrJwtTokenFilter(); } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager, UserAuthenticatingFilter userAuthenticatingFilter, UserAuthenticatingOrJwtTokenFilter userAuthenticatingOrJwtTokenFilter) { // 创建ShiroFilterFactoryBean对象 ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); // 设置SecurityManager factoryBean.setSecurityManager(securityManager); // 设置自定义过滤器 Map<String, Filter> filters = new HashMap<>(); filters.put("userAuthenticatingFilter", userAuthenticatingFilter); filters.put("userAuthenticatingOrJwtTokenFilter", userAuthenticatingOrJwtTokenFilter); factoryBean.setFilters(filters); // 设置过滤器链 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/**", "userAuthenticatingOrJwtTokenFilter"); factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return factoryBean; } // ... } ``` 在上述代码中,我们通过@Bean注解创建了两个自定义过滤器实例:UserAuthenticatingFilter和UserAuthenticatingOrJwtTokenFilter,然后将这两个过滤器添加到ShiroFilterFactoryBean对象中,并设置过滤器链。 4. 在Controller中使用Shiro进行身份认证: ``` @RestController public class UserController { @RequestMapping(value = "/login", method = RequestMethod.POST) public String login(String username, String password) { Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); return "login success"; } catch (AuthenticationException e) { return "login failed"; } } @RequestMapping(value = "/logout") public String logout() { Subject subject = SecurityUtils.getSubject(); subject.logout(); return "logout success"; } @RequestMapping(value = "/test") public String test() { return "test success"; } } ``` 在上述代码中,我们通过调用SecurityUtils.getSubject()获取当前Subject对象,然后使用UsernamePasswordToken进行身份认证。如果身份认证成功,则返回"login success"字符串;否则返回"login failed"字符串。 5. 在Postman或浏览器中访问API: - 访问/login接口进行身份认证,例如: ``` POST http://localhost:8080/login?username=admin&password=admin ``` - 访问/test接口进行访问控制,例如: ``` GET http://localhost:8080/test ``` 如果用户已经登录,则返回"test success"字符串;否则返回"401 Unauthorized"错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

可——叹——落叶飘零

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

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

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

打赏作者

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

抵扣说明:

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

余额充值