springboot下使用shiro自定义filter的个人经验分享

目录

  • 目标
  • 步骤
    • 1.在pom.xml中添加shiro的依赖
    • 2.创建ShiroRealm.java
    • 3.创建ShiroConfiguration.java
    • 4.创建自定义的过滤器MyFilter.java
    • 5.步骤3中使用了自定义密码验证的方式
    • 6.步骤3中放开了对登录页/loginController的过滤
  • 个人经验
    在springboot中使用shiro,由于没有了xml配置文件,因此使用的方法与spring中有些区别。在踩了无数个坑后,在此将springboot下使用shiro的步骤总结如下。
    由于本人对shiro的了解不是很深入,在实现了工作需求后就没有继续研究了,因此可能存在遗漏的地方或有错误的地方,还请多包涵。

    目标
    • 在springboot中使用shiro
    • 1.实现用户的登录验证
    • 2.对于一些指定的url使用自定义的filter验证方式(不再使用shiro的realm验证)

    步骤
    1.在pom.xml中添加shiro的依赖
    org.apache.shiroshiro-spring1.3.2


    2.创建ShiroRealm.java
    继承AuthorizingRealm类,重写登录认证方法与授权方法
    import User; import UserService; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; public class ShiroRealm extends AuthorizingRealm {//自己编写的service,注入,执行数据库查询方法用@Autowiredprivate UserService userService; //认证.登录@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {UsernamePasswordToken utoken=(UsernamePasswordToken) token; //获取用户输入的tokenString username = utoken.getUsername(); //这个User对象为自定义的JavaBean,使用userService从数据库中得到User对象(包含用户名、密码、权限3个字段)User user = userService.findUserByUserName(username); return new SimpleAuthenticationInfo(user, user.getPassword(),this.getClass().getName()); //放入shiro.调用CredentialsMatcher检验密码}//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {User user=(User) principal.fromRealm(this.getClass().getName()).iterator().next(); //获取session中的用户List permissions=new ArrayList<>(); //使用自定义的user对象获得权限字段,string类型,装入集合permissions.add(user.getRole()); SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); info.addStringPermissions(permissions); //将权限放入shiro中.(我的代码中其实没有用到权限相关)return info; }}


    3.创建ShiroConfiguration.java
    使用@Configuration注解,是shiro的配置类,类似xml
    import java.util.LinkedHashMap; import java.util.Map; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; @Configurationpublic class ShiroConfiguration { @Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){ShiroFilterFactoryBean shiroFilterFactoryBean= new ShiroFilterFactoryBean(); // 必须设置 SecurityManagershiroFilterFactoryBean.setSecurityManager(securityManager); /*重要,设置自定义拦截器,当访问某些自定义url时,使用这个filter进行验证*/Map filters = new LinkedHashMap(); //如果map里面key值为authc,表示所有名为authc的过滤条件使用这个自定义的filter//map里面key值为myFilter,表示所有名为myFilter的过滤条件使用这个自定义的filter,具体见下方filters.put("myFilter", new MyFilter()); shiroFilterFactoryBean.setFilters(filters); /*---------------------------------------------------*/ //拦截器Map filterChainDefinitionMap = new LinkedHashMap(); //配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了filterChainDefinitionMap.put("/logout", "logout"); //anon:所有url都都可以匿名访问; //authc: 需要认证才能进行访问; //user:配置记住我或认证通过可以访问;//放开静态资源的过滤filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/img/**", "anon"); //放开登录url的过滤filterChainDefinitionMap.put("/loginController", "anon"); /////对于指定的url,使用自定义filter进行验证filterChainDefinitionMap.put("/targetUrl", "myFilter"); //可以配置多个filter,用逗号分隔,按顺序过滤,下方表示先通过自定义filter的验证,再通过shiro默认过滤器的验证//filterChainDefinitionMap.put("/targetUrl", "myFilter,authc"); /////过滤链定义,从上向下顺序执行,一般将 /**放在最为下边//url从上向下匹配,当条件匹配成功时,就会进入指定filter并return(不会判断后续的条件),因此这句需要在最下边filterChainDefinitionMap.put("/**", "authc"); //如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面shiroFilterFactoryBean.setLoginUrl("/login"); // 登录成功后要跳转的链接shiroFilterFactoryBean.setSuccessUrl("/loginSuccess"); // 未授权界面shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Beanpublic SecurityManager securityManager(){DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager(); return securityManager; }//配置核心安全事务管理器@Bean(name="securityManager")public SecurityManager securityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {DefaultWebSecurityManager manager=new DefaultWebSecurityManager(); manager.setRealm(shiroRealm); return manager; }//配置自定义的权限登录器@Bean(name="shiroRealm")public ShiroRealm shiroRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) {ShiroRealm shiroRealm=new ShiroRealm(); shiroRealm.setCredentialsMatcher(matcher); return shiroRealm; }//配置自定义的密码比较器@Bean(name="credentialsMatcher")public CredentialsMatcher credentialsMatcher() {return new CredentialsMatcher(); }}


    4.创建自定义的过滤器MyFilter.java
    【springboot下使用shiro自定义filter的个人经验分享】继承AccessControlFilter类,在步骤3中使用
    import org.apache.commons.lang.StringUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.AccessControlFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.util.Enumeration; import java.util.Properties; import java.util.Set; public class MyFilter extends AccessControlFilter {private static Logger log = LoggerFactory.getLogger(MyFilter.class); //判断是否拦截,false为拦截,true为允许@Overridepublic boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object object) throws Exception {Subject subject = getSubject(req,resp); String url = getPathWithinApplication(req); log.info("当前用户正在访问的url为 " + url); log.info("subject.isPermitted(url); "+subject.isPermitted(url)); //可自行根据需要判断是否拦截,可以获得subject判断用户权限,也可以使用req获得请求头请求体信息//return true; return false; } //上面的方法返回false后(被拦截),会进入这个方法;这个方法返回false表示处理完毕(不放行);返回true表示需要继续处理(放行)@Overridepublic boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {//从req中获得的值,也可以自己使用其它判断是否放行的方法String username = request.getParameter("name"); String password = request.getParameter("password"); //创建token对象UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(username,password); Subject subject = SecurityUtils.getSubject(); try {//使用subject对象的login方法验证该token能否登录(使用方法2中的shiroRealm.java中的方法验证)subject.login(usernamePasswordToken); } catch (Exception e) {//log.info("登陆失败"); //log.info(e.getMessage()); return false; }//log.info("登陆成功"); return true; }}


    5.步骤3中使用了自定义密码验证的方式
    因此需要创建类CredentialsMatcher.java(与步骤3中的名称对应),继承SimpleCredentialsMatcher类
    import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.SimpleCredentialsMatcher; public class CredentialsMatcher extends SimpleCredentialsMatcher { @Overridepublic boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {UsernamePasswordToken utoken=(UsernamePasswordToken) token; //获得用户输入的密码:(可以采用加盐(salt)的方式去检验)String inPassword = new String(utoken.getPassword()); //获得数据库中的密码String dbPassword=(String) info.getCredentials(); //进行密码的比对return this.equals(inPassword, dbPassword); }}


    6.步骤3中放开了对登录页/loginController的过滤
    因此我增加了一个ShiroController.java类
    import User; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession; @Controllerpublic class ShiroController {@RequestMapping("/")public String loginPage() {return "login"; }@RequestMapping("/login")public String login() {return "login"; }@RequestMapping("/loginController")public String loginUser(String username,String password,HttpSession session) {UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(username,password); Subject subject = SecurityUtils.getSubject(); try {subject.login(usernamePasswordToken); //完成登录//自定义的JavaBean,用于保存用户名、密码、权限3个字段User user=(User) subject.getPrincipal(); //可选,可放入session,以备后续使用session.setAttribute("user", user); //跳转到登录成功页(controller)return "forward:/loginSuccess"; } catch(Exception e) {//登录失败,跳转回登录页(html)return "login"; } }@RequestMapping("/logOut")public String logOut(HttpSession session) {Subject subject = SecurityUtils.getSubject(); subject.logout(); //session.removeAttribute("user"); return "login"; }}


    个人经验 1.shiro配置类中的url拦截的执行顺序为从上到下,如果url匹配到一个规则,则会跳出匹配方法,忽略后续的匹配规则(相当于return)。
    2.shiro使用自定义filter时,最好继承shiro的filter,不要直接继承Filter类。
    3.shiro使用自定义filter时,map集合的key配置为"authc"、value配置为"new MyFilter()"时,表示对配置为authc的url使用自定义filter进行拦截,而不会使用ShiroRealm中的验证方法验证(可能是将shiro默认的authc的拦截器覆盖了);因此最好将key配置为其它自定义的字符串,将部分url的拦截规则设置为使用自定义filter拦截即可(如果仍想使用shiro默认的拦截器,可用逗号连接"authc")。
    以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

      推荐阅读