欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

程序员文章站 2022-04-08 19:55:05
本文是接着上篇博客写的:Spring boot 入门(三):SpringBoot 集成结合 AdminLTE(Freemarker),利用 generate 自动生成代码,利用 DataTable 和 PageHelper 进行分页显示。按照前面的博客,已经可以搭建一个简单的 Spring Boot ......

本文是接着上篇博客写的:spring boot 入门(三):springboot 集成结合 adminlte(freemarker),利用 generate 自动生成代码,利用 datatable 和 pagehelper 进行分页显示。按照前面的博客,已经可以搭建一个简单的 spring boot 系统,本篇博客继续对此系统进行改造,主要集成了 shiro 权限认证框架,关于 shiro 部分,在本人之前的博客(认证与shiro安全框架)有介绍到,这里就不做累赘的介绍。

此系列的博客为实践部分,以代码和搭建系统的过程为主,如遇到专业名词,自行查找其含义。

1.shiro 配置类

系统搭建到目前为止,主要用到了3个配置类,均与 shiro 有关,后期随着项目的扩大,配置文件也会随之增多。

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

 

  • freemarkerconfig:主要针对 freemarker 页面显示的配置,关于 shiro 部分,为 shiro 标签设置了共享变量,如果不设置此变量,freemarker 页面将不能识别 shiro 的标签,其主要代码如下:
1 configuration.setsharedvariable("shiro", new shirotags());
  • mshirofilterfactorybean:设置了过滤器,当然也可以在 config 文件里面配置过滤器,其缺点是:在每次请求里面都做了 session 的读取和更新访问时间等操作,这样在集群部署 session 共享的情况下,数量级的加大了处理量负载。本项目后期将用到分布式,因此这里就直接将过滤器与 config 配置文件分离,提高效率。
 1 private final class mspringshirofilter extends abstractshirofilter {
 2         protected mspringshirofilter(websecuritymanager websecuritymanager, filterchainresolver resolver) {
 3             super();
 4             if (websecuritymanager == null) {
 5                 throw new illegalargumentexception("websecuritymanager property cannot be null.");
 6             }
 7             setsecuritymanager(websecuritymanager);
 8             if (resolver != null) {
 9                 setfilterchainresolver(resolver);
10             }
11         }
12 
13         @override
14         protected void dofilterinternal(servletrequest servletrequest, servletresponse servletresponse,
15                                         filterchain chain) throws servletexception, ioexception {
16             httpservletrequest request = (httpservletrequest) servletrequest;
17             string str = request.getrequesturi().tolowercase();
18             boolean flag = true;
19             int idx = 0;
20             if ((idx = str.indexof(".")) > 0) {
21                 str = str.substring(idx);
22                 if (ignoreext.contains(str.tolowercase()))
23                     flag = false;
24             }
25             if (flag) {
26                 super.dofilterinternal(servletrequest, servletresponse, chain);
27             } else {
28                 chain.dofilter(servletrequest, servletresponse);
29             }
30         }
31 
32     }
  • shiroconfiguration:通用配置文件,此配置文件为 shiro 的基础通用配置文件,只要是集成 shiro,必有此文件,主要配置 shiro 的登录认证相关的信息,其代码如下:
  1 /**
  2      * 设置shiro的缓存,缓存参数均配置在xml文件中
  3      * @return
  4      */
  5     @bean
  6     public ehcachemanager getehcachemanager() {
  7         ehcachemanager em = new ehcachemanager();
  8         em.setcachemanagerconfigfile("classpath:ehcache/ehcache-shiro.xml");  
  9         return em;  
 10     }
 11     /**
 12      * 凭证匹配器
 13      * (由于我们的密码校验交给shiro的simpleauthenticationinfo进行处理了
 14      *  所以我们需要修改下dogetauthenticationinfo中的代码;
 15      * )
 16      * @return
 17      */
 18     @bean
 19     public hashedcredentialsmatcher hashedcredentialsmatcher(){
 20        hashedcredentialsmatcher hashedcredentialsmatcher = new hashedcredentialsmatcher();
 21        hashedcredentialsmatcher.sethashalgorithmname("md5");//散列算法:这里使用md5算法;
 22        hashedcredentialsmatcher.sethashiterations(1);//散列的次数,比如散列两次,相当于 md5(md5(""));
 23        return hashedcredentialsmatcher;
 24     }
 25     /**
 26      * 
 27      * 主文件
 28      */
 29     @bean(name = "myshirorealm")
 30     public userrealm myshirorealm(ehcachemanager cachemanager) {
 31         userrealm realm = new userrealm(); 
 32         realm.setcachemanager(cachemanager);
 33         realm.setcredentialsmatcher(hashedcredentialsmatcher());
 34         return realm;
 35     }
 36    //会话id生成器
 37     @bean(name = "sessionidgenerator")
 38     public javauuidsessionidgenerator javauuidsessionidgenerator(){
 39         javauuidsessionidgenerator javauuidsessionidgenerator = new javauuidsessionidgenerator();
 40         return javauuidsessionidgenerator;
 41     }
 42     @bean(name = "sessionidcookie")
 43     public simplecookie getsessionidcookie(){
 44         simplecookie sessionidcookie = new simplecookie("sid");
 45         sessionidcookie.sethttponly(true);
 46         sessionidcookie.setmaxage(-1);
 47         return sessionidcookie;
 48         
 49     }
 50     /*<!-- 会话dao -->*/
 51     @bean(name = "sessiondao")
 52     public enterprisecachesessiondao enterprisecachesessiondao(){
 53         enterprisecachesessiondao sessiondao = new enterprisecachesessiondao();
 54         sessiondao.setsessionidgenerator(javauuidsessionidgenerator());
 55         sessiondao.setactivesessionscachename("shiro-activesessioncache");
 56         return sessiondao;
 57     }
 58     @bean(name = "sessionvalidationscheduler")
 59     public executorservicesessionvalidationscheduler getexecutorservicesessionvalidationscheduler() {
 60         executorservicesessionvalidationscheduler scheduler = new executorservicesessionvalidationscheduler();
 61         scheduler.setinterval(1800000);
 62         return scheduler;
 63     }
 64     @bean(name = "sessionmanager")
 65     public defaultwebsessionmanager sessionmanager(enterprisecachesessiondao sessiondao){
 66         defaultwebsessionmanager sessionmanager = new defaultwebsessionmanager();
 67         sessionmanager.setglobalsessiontimeout(1800000);
 68         sessionmanager.setdeleteinvalidsessions(true);
 69         sessionmanager.setsessionvalidationschedulerenabled(true);
 70         sessionmanager.setsessionvalidationscheduler(getexecutorservicesessionvalidationscheduler());
 71         sessionmanager.setsessiondao(sessiondao);
 72         sessionmanager.setsessionidcookieenabled(true);
 73         sessionmanager.setsessionidcookie(getsessionidcookie());
 74         return sessionmanager;
 75     }
 76     @bean(name = "lifecyclebeanpostprocessor")
 77     public lifecyclebeanpostprocessor getlifecyclebeanpostprocessor() {
 78         return new lifecyclebeanpostprocessor();
 79     }
 80     @bean
 81     public defaultadvisorautoproxycreator getdefaultadvisorautoproxycreator() {
 82         defaultadvisorautoproxycreator daap = new defaultadvisorautoproxycreator();
 83         daap.setproxytargetclass(true);
 84         return daap;
 85     }
 86     @bean(name = "securitymanager")
 87     public defaultwebsecuritymanager getdefaultwebsecuritymanager(userrealm myshirorealm, defaultwebsessionmanager sessionmanager) {
 88         defaultwebsecuritymanager dwsm = new defaultwebsecuritymanager();
 89         dwsm.setrealm(myshirorealm);
 90 //      <!-- 用户授权/认证信息cache, 采用ehcache 缓存 --> 
 91         dwsm.setcachemanager(getehcachemanager());
 92         dwsm.setsessionmanager(sessionmanager);
 93         return dwsm;
 94     }
 95     /**
 96      *  开启shiro aop注解支持.
 97      *  使用代理方式;所以需要开启代码支持;
 98      * @param securitymanager
 99      * @return
100      */
101     @bean
102     public authorizationattributesourceadvisor getauthorizationattributesourceadvisor(defaultwebsecuritymanager securitymanager) {
103         authorizationattributesourceadvisor aasa = new authorizationattributesourceadvisor();
104         aasa.setsecuritymanager(securitymanager);
105         return aasa;
106     }
107     /**
108      * shirofilter<br/>
109      * 注意这里参数中的 studentservice 和 iscoredao 只是一个例子,因为我们在这里可以用这样的方式获取到相关访问数据库的对象,
110      * 然后读取数据库相关配置,配置到 shirofilterfactorybean 的访问规则中。实际项目中,请使用自己的service来处理业务逻辑。
111      *
112      */
113     @bean(name = "shirofilter")
114     public shirofilterfactorybean getshirofilterfactorybean(defaultwebsecuritymanager securitymanager) {
115         shirofilterfactorybean shirofilterfactorybean = new mshirofilterfactorybean();
116         // 必须设置 securitymanager  
117         shirofilterfactorybean.setsecuritymanager(securitymanager);
118         // 如果不设置默认会自动寻找web工程根目录下的"/login.jsp"页面
119         shirofilterfactorybean.setloginurl("/login");
120         // 登录成功后要跳转的连接
121         shirofilterfactorybean.setsuccessurl("/certification");
122         //shirofilterfactorybean.setsuccessurl("/index");
123         shirofilterfactorybean.setunauthorizedurl("/403");
124         loadshirofilterchain(shirofilterfactorybean);
125         return shirofilterfactorybean;
126     }
127     /**
128      * 加载shirofilter权限控制规则(从数据库读取然后配置)
129      *
130      */
131     private void loadshirofilterchain(shirofilterfactorybean shirofilterfactorybean){
132         /////////////////////// 下面这些规则配置最好配置到配置文件中 ///////////////////////
133         map<string, string> filterchaindefinitionmap = new linkedhashmap<string, string>();
134         // authc:该过滤器下的页面必须验证后才能访问,它是shiro内置的一个拦截器org.apache.shiro.web.filter.authc.formauthenticationfilter
135         filterchaindefinitionmap.put("/login", "authc");
136         filterchaindefinitionmap.put("/logout", "logout");
137         // anon:它对应的过滤器里面是空的,什么都没做
138         logger.info("##################从数据库读取权限规则,加载到shirofilter中##################");
139 //        filterchaindefinitionmap.put("/user/edit/**", "authc,perms[user:edit]");// 这里为了测试,固定写死的值,也可以从数据库或其他配置中读取
140         shirofilterfactorybean.setfilterchaindefinitionmap(filterchaindefinitionmap);
141     }

2.登录认证与权限管理

主要重写了 realm域,完成权限认证和权限管理:

 1     protected authorizationinfo dogetauthorizationinfo(principalcollection principals) {
 2         //如果没有做权限验证,此处只需要return null即可
 3         simpleauthorizationinfo authorizationinfo = new simpleauthorizationinfo();
 4         string username = (string) principals.getprimaryprincipal();
 5         result<tuser> list = userservice.getuserbyusername(username);
 6         if(list.isstatus()) {
 7             //获取该用户所属的角色
 8             result<list<trole>> resultrole = roleservice.getrolebyuserid(list.getresultdata().getuserid());
 9             if(resultrole.isstatus()) {
10                 hashset<string> role = new hashset<string>();
11                 for(trole trole : resultrole.getresultdata()) {
12                     role.add(trole.getroleid()+"");
13                 }
14                 //获取该角色拥有的权限
15                 result<list<tpermission>> resultpermission = permissionservice.getpermissionsbyroleid(role);
16                 if(resultpermission.isstatus()) {
17                     hashset<string> permissions = new hashset<string>();
18                     for(tpermission tpermission : resultpermission.getresultdata()) {
19                         permissions.add(tpermission.getpermissionsvalue());
20                     }
21                     system.out.println("权限:"+permissions);
22                     authorizationinfo.setstringpermissions(permissions);
23                 }
24             }
25         }
26         //return null;
27         return authorizationinfo;
28     }
29 
30     @override
31     protected authenticationinfo dogetauthenticationinfo(authenticationtoken authenticationtoken) throws authenticationexception {
32         //认证登录
33         string username = (string) authenticationtoken.getprincipal();
34         //string password = new string((char[]) authenticationtoken.getcredentials());
35         result<tuser> result = userservice.getuserbyusername(username);
36         if (result.isstatus()) {
37             tuser user = result.getresultdata();
38             return new simpleauthenticationinfo(user.getusername(), user.getpassword(), getname());
39         }
40         //return new simpleauthenticationinfo(user., "123456", getname());
41         return null;
42     }
43 }

2.1.登录认证

首先创建一个前端登录界面,做一个简单的登录 form 表单

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

点击登录即想后台发送一个请求,必须是post请求,否则shiro不能识别,认证部分主要在 ream 中完成,新建一个类,继承 authorizingrealm ,然后在重写 dogetauthenticationinfo 方法:

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理 
只需要通过界面上的用户名查找到数据库存储的相关信息即可,具体的认证是 shiro 内部自己完成的,我们只需要传入数据库中存储的用户名和密码个认证函数即可(new simpleauthenticationinfo(user.getusername(), user.getpassword(), getname())),我们可以自己重新定义密码比较器,密码比较器的写法较多,在认证与shiro安全框架中,直接将密码比较器写入到ream中,耦合度太高,本项目通过配置的方式重写密码比较器,具体代码请参考参考shiroconfiguration配置类:
Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

 

在具体的 login 方法中,写入一些登录失败的异常即可,主要用户将此失败结果存入 session,并显示在页面上:
 1     @requestmapping(value = "/login", method = requestmethod.post)
 2     public string postlogin(redirectattributes redirectattributes, httpservletrequest request, httpsession session) {
 3         // 登录失败从request中获取shiro处理的异常信息。
 4         // shirologinfailure:就是shiro异常类的全类名.
 5         string exception = (string) request.getattribute("shirologinfailure");
 6 
 7         system.out.println("exception=" + exception);
 8         string msg = "";
 9         if (exception != null) {
10             if (unknownaccountexception.class.getname().equals(exception)) {
11                 system.out.println("unknownaccountexception -- > 账号不存在:");
12                 msg = "用户不存在!";
13             } else if (incorrectcredentialsexception.class.getname().equals(exception)) {
14                 system.out.println("incorrectcredentialsexception -- > 密码不正确:");
15                 msg = "密码不正确!";
16             } else if ("kaptchavalidatefailed".equals(exception)) {
17                 system.out.println("kaptchavalidatefailed -- > 验证码错误");
18                 msg = "验证码错误!";
19             } else {
20                 //msg = "else >> "+exception;
21                 msg = "密码不正确!";
22                 system.out.println("else -- >" + exception);
23             }
24         }
25         redirectattributes.addflashattribute("msg", msg);
26         session.setattribute("msg", msg);
27         //return redirect("/login");
28         return "redirect:login";
29         //return msg;
30     }

此时登录认证部门已经完成:一个页面+后台2个函数(1个认证函数+1个login函数)

2.2.权限管理

总体来说,权限管理只需要在界面增加 shiro 的权限标签即可,可以使用角色的标签,也可以使用权限的标签,一般情况下2种标签配合使用,效果最好 <@shiro.haspermission name="xtgl-yhgl:read"> <@shiro.hasrolen name="xtgl-yhgl:read">

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

 

此外,在 realm 中,需要重写权限认证的业务逻辑,通常情况下通过用户 id 找到该用户所属的角色,然后通过角色 id 找到该角色拥有的权限,并将角色或者权限写入的 shiro 中即可: authorizationinfo.setstringpermissions(permissions); authorizationinfo.setroles(role);

本项目也是通过此逻辑完成权限管理的

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

上面2张截图表示的是一个函数。

到此,spring boot集成shiro框架的权限认证已经搭建完毕,可以实现简单的权限管理。

3.新增文件

较上一篇博客,shiro 部分新增加的文件

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

 

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理