好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

在springboot中使用shiro,由于没有了xml配置文件,因此使用的方法与spring中有些区别。在踩了无数个坑后,在此将springboot下使用shiro的步骤总结如下。

由于本人对shiro的了解不是很深入,在实现了工作需求后就没有继续研究了,因此可能存在遗漏的地方或有错误的地方,还请多包涵。

目标

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

步骤

1.在pom.xml中添加shiro的依赖

?

1

2

3

4

5

< dependency >

    < groupId >org.apache.shiro</ groupId >

    < artifactId >shiro-spring</ artifactId >

    < version >1.3.2</ version >

</ dependency >

2.创建ShiroRealm.java

继承AuthorizingRealm类,重写登录认证方法与授权方法

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

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,注入,执行数据库查询方法用

     @Autowired

     private UserService userService;

     //认证.登录

     @Override

     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

         UsernamePasswordToken utoken=(UsernamePasswordToken) token; //获取用户输入的token

         String username = utoken.getUsername();

         //这个User对象为自定义的JavaBean,使用userService从数据库中得到User对象(包含用户名、密码、权限3个字段)

         User user = userService.findUserByUserName(username);

         return new SimpleAuthenticationInfo(user, user.getPassword(), this .getClass().getName()); //放入shiro.调用CredentialsMatcher检验密码

     }

     //授权

     @Override

     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {

         User user=(User) principal.fromRealm( this .getClass().getName()).iterator().next(); //获取session中的用户

         List<String> 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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

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;

 

@Configuration

public class ShiroConfiguration {

     @Bean

     public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){

         ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();

         // 必须设置 SecurityManager

         shiroFilterFactoryBean.setSecurityManager(securityManager);

 

         /*重要,设置自定义拦截器,当访问某些自定义url时,使用这个filter进行验证*/

         Map<String, Filter> filters = new LinkedHashMap<String, Filter>();

         //如果map里面key值为authc,表示所有名为authc的过滤条件使用这个自定义的filter

         //map里面key值为myFilter,表示所有名为myFilter的过滤条件使用这个自定义的filter,具体见下方

         filters.put( "myFilter" , new MyFilter());

         shiroFilterFactoryBean.setFilters(filters);

         /*---------------------------------------------------*/

 

         //拦截器

         Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();

         //配置退出过滤器,其中的具体的退出代码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;

     }

 

     @Bean

     public 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

继承AccessControlFilter类,在步骤3中使用

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

import org.apache测试数据mons.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为允许

     @Override

     public 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表示需要继续处理(放行)

     @Override

     public 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类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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 {

 

     @Override

     public 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类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

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;

 

@Controller

public 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")。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://blog.csdn.net/BHSZZY/article/details/85328353

查看更多关于springboot下使用shiro自定义filter的个人经验分享的详细内容...

  阅读:28次