Shiro
Shiro概述
Apache Shiro 是Java 的一个安全框架。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与Web 集成、缓存等。
基本功能
1. Authentication 【ɔː,θentɪ'keɪʃən/】
身份认证/登录,验证用户是不是拥有相应的身份; 身份 凭证
2. Authorization【/ɔːθəraɪ'zeɪʃ(ə)n/】
授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
3. Session Manager
会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
4. Cryptography【/krɪp'tɒgrəfɪ/】
加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
5. Web Support
Web 支持,可以非常容易的集成到Web 环境;
6. Caching
缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
7. Concurrency【/kən'kɚrənsi】
shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能 把权限自动传播过去;
8. Testing
提供测试支持;
9. Run As
允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
10. Remember Me
记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
注意:Shiro不会去维护用户、维护权限,这些需要我们自己去设计/提供,然后通过相应的接口注入给Shiro即可。
架构说明
1. Subject
Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权
2. SecurityManager【/sɪ'kjʊərətɪ/】
SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。
3. Authenticator【/ɔ'θɛntɪ,ketɚ/】
Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
4. Authorizer
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
5. realm
Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。
6. sessionManager
sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
7. SessionDAO
SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。
8. CacheManager
CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
9. Cryptography【/krɪp'tɒgrəfɪ/】
Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
shiro的下载
下载地址:http://shiro.apache.org/
shiro包介绍
shiro-all 所有shiro的包全导
shiro-core 核心包
shiro-web 集合WEB环境需要的包
shiro-ehcache 缓存处理的包
shiro-quartz 定时任务
shiro-spring 集成spring的包
shiro-spring-boot-starter 集成springboot的java项目的包
shiro-spring-boot-web-starter 集成springboot的web项目的包
shiro.ini 文件
Shiro.ini 文件的说明
-
ini (InitializationFile) 初始文件.Window系统文件扩展名
-
Shiro 使用时可以连接数据库,也可以不连接数据库
2.1 如果不连接数据库,可以在shiro.ini中配置静态数据
Shrio.ini 文件的组成部分
1. [main]:定义全局变量
-
内置securityManager对象.
-
操作内置对象时,在[main]里面写东西
[main] securityManager.属性=值 myobj=com.bjsxt.lei securityManager.对象属性=$myobj
2. [users]:定义用户名和密码
[users] # 定义用户名为zhangsan 密码为zs zhangsan=zs # 定义用户名lisi密码为lisi同时具有role1和role2两个角色 lisi=lisi,role1,role2
3. [roles]:定义角色
[roles] role1=权限名1,权限名2 role2=权限3,权限4
4. [urls]:定义哪些内置urls生效(在web应用时使用)
[urls] #url地址 = 内置filter或自定义filter #访问时出现/login的url必须去认证.支持authc对应的Filter /login = authc #任意的url都不需要进行认证等功能. /** = anon #所有的内容都必须保证用户已经登录. /** = user #url abc 访问时必须保证用户具有role1和role2角色. /abc = roles[“role1,role2”]
过滤器
过滤器名称 | 过滤器类 | 描述 |
---|---|---|
anon | org.apache.shiro.web.filter.authc.AnonymousFilter | 匿名过滤器 |
authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter | 如果继续操作,需要做对应的表单验证否则不能通过 |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter | 基本http验证过滤,如果不通过,跳转屋登录页面 |
logout | org.apache.shiro.web.filter.authc.LogoutFilter | 登录退出过滤器 |
noSessionCreation | org.apache.shiro.web.filter.session.NoSessionCreationFilter | 没有session创建过滤器 |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter | 权限过滤器 |
port | org.apache.shiro.web.filter.authz.PortFilter | 端口过滤器,可以设置是否是指定端口如果不是跳转到登录页面 |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter | http方法过滤器,可以指定如post不能进行访问等 |
roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter | 角色过滤器,判断当前用户是否指定角色 |
ssl | org.apache.shiro.web.filter.authz.SslFilter | 请求需要通过ssl,如果不是跳转回登录页 |
user | org.apache.shiro.web.filter.authc.UserFilter | 如果访问一个已知用户,比如记住我功能,走这个过滤器 |
anon:某一个路径是否需要认证之后才能访问
/login/doLogin*=anon #代表如果用户请求地址为/login/doLogin* 就不用登陆就可以访问 |--http://127.0.0.1:8080/shiro/login/doLogin.action /**=anon #代表如果所有地址者不用登陆就可以访问 |--http://127.0.0.1:8080/shiro/aaa.action /**=authc #所有路径都人认证之后才能访问 |--http://127.0.0.1:8080/bjsxt/aaa.action
Shiro实现认证【使用shiro.ini】
基本概念
1. 身份验证
即在应用中谁能证明他就是他本人。一般提供如他们的身份ID 一些标识信息来,表明他就是他本人,如提供身份证,用户名/密码来证明。
在 shiro 中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能验证用户身份。
2. principals【/'prɪnsəpl】
身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
3. credentials【/krə'dɛnʃlz/】
证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
最常见的principals和credentials组合就是用户名/密码了。接下来先进行一个基本的身份认证
认证流程
其它就是使用Shrio的认证来取代我们传统的登陆方式。
创建Maven项目
在main目录下创建resources的资源配置文件目录
在resources目录下面创建shiro.ini
修改pom.xml引入shiro的依赖
<!--加入shiro的依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.5.0</version> </dependency> <!--日志包--> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
创建log4j.properties
log4j.rootLogger=debug, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
创建代码测试
https://github.com/apache/shiro/blob/master/samples/quickstart/src/main/java/Quickstart.java
package com.sxt; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; /** * Hello world! */ public class App { public static void main(String[] args) { String username = "zhangsan"; String password = "123456"; // 1.创建securityManager工厂 SecurityManager JDK也有这个类,在java.lang包 注意不要使用jdk里面那个类 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //从工厂里面得到一个 serurityManager SecurityManager securityManager = factory.getInstance(); //把当前的安全管理器绑定到当前线程 SecurityUtils.setSecurityManager(securityManager); //得到当前的Object Subject subject = SecurityUtils.getSubject(); //封装用户名和密码到token UsernamePasswordToken token = new UsernamePasswordToken(username, password); //进行登陆 try { subject.login(token);//进行认证,如果失败会抛异常 System.out.println("登陆成功"); // }catch (IncorrectCredentialsException e){ // System.out.println("密码不正确"); // }catch (UnknownAccountException e){ // System.out.println("用户名不存在"); } catch (AuthenticationException e) { System.out.println("用户名或密码正确"); } checkUserIsLogin(); } private static void checkUserIsLogin() { Subject subject = SecurityUtils.getSubject(); subject.isAuthenticated();//判断当前线程里面的Subject是否退出 System.out.println("是否认证通过:" + subject.isAuthenticated()); System.out.println("退出"); subject.logout();//退出 System.out.println("是否认证通过:" + subject.isAuthenticated()); } }
Shiro实现授权【使用shiro.ini】
1. 授权概述
授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。
2. 关键对象介绍
主体
主体,即访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。
资源
在应用中用户可以访问的任何东西,比如访问JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
权限
安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制)打印文档等等。
角色
角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。
3. 授权流程
相关方法说明
subject.hasRole(“”); //判断是否有角色 subject.hashRoles(List); //分别判断用户是否具有List中每个内容 subject.hasAllRoles(Collection); //返回boolean,要求参数中所有角色用户都需要具有. subject.isPermitted(“”); //判断是否具有权限
修改pom.xml
<!--加入shiro的依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.5.0</version> </dependency> <!--日志包--> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
配置shiro.ini
#配置用户名和密码 [users] zhangsan=123456,role1 lisi=123456,role2,role3 wangwu=123456,role4 #配置角色 [roles] role1=user:query,user:add,user:update,user:delete,user:exprot role2=user:query role3=user:query,user:add,user:update,user:delete role4=user:query,user:add,user:update
创建代码测试
package com.sxt; import com.sun.javafx.font.freetype.HBGlyphLayout; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Hello world! */ public class App { public static void main(String[] args) { String username = "zhangsan"; String password = "123456"; // 1.创建securityManager工厂 SecurityManager JDK也有这个类,在java.lang包 注意不要使用jdk里面那个类 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //从工厂里面得到一个serurityManager SecurityManager securityManager = factory.getInstance(); //把当前的安全管理器绑定到当前线程 SecurityUtils.setSecurityManager(securityManager); //得到当前的Object Subject subject = SecurityUtils.getSubject(); //封装用户名和密码到token UsernamePasswordToken token = new UsernamePasswordToken(username, password); //进行登陆 try { subject.login(token);//进行认证,如果失败会抛异常 System.out.println("登陆成功"); // }catch (IncorrectCredentialsException e){ // System.out.println("密码不正确"); // }catch (UnknownAccountException e){ // System.out.println("用户名不存在"); } catch (AuthenticationException e) { System.out.println("用户名或密码正确"); } //判断是否认证通过 if (subject.isAuthenticated()) { //角色相关 boolean role1 = subject.hasRole("role1"); System.out.println("判断当前登陆用户是否有role1的角色:" + role1); List<String> roles = new ArrayList<>(); roles.add("role1"); roles.add("role2"); // roles.add("role3"); // roles.add("role4"); boolean[] hasRoles = subject.hasRoles(roles); System.out.println("分别判断roles集合里面的角色是否存在返回数组:" + Arrays.toString(hasRoles)); boolean b = subject.hasAllRoles(roles); System.out.println("判断当前登陆用户是否同时拥有roles里面的所有角色:" + b); //权限相关的 boolean permitted = subject.isPermitted("user:export"); System.out.println("判断当前登陆用户是否有user:export的权限" + permitted); boolean[] permitted1 = subject.isPermitted("user:query", "user:add", "user:export"); System.out.println("判断当前登陆用户是否分别拥有某些权限:" + Arrays.toString(permitted1)); boolean permittedAll = subject.isPermittedAll("user:query", "user:add", "user:export"); System.out.println("判断当前登陆用户是否同时拥有某些权限:" + permittedAll); } else { System.out.println("您未登陆"); } } }
自定义Realm实现认证
Shiro默认使用自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。
Realm接口
IniRealm的执行流程
解析shiro.ini
做认证和授权
总结
如果自定义realm只想做认证,继承AuthenticationRealm
如果自定义realm认证和授权要一起做,继承AuthorizingRealm
自定义realm做认证
创建项目
修改pom.xml
<!--加入shiro的依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.5.0</version> </dependency> <!--日志包--> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
创建User
package com.sxt.domain; public class User { private Integer id; private String loginname; private String username; private String password; public User() { } public User(Integer id, String loginname, String username, String password) { this.id = id; this.loginname = loginname; this.username = username; this.password = password; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLoginname() { return loginname; } public void setLoginname(String loginname) { this.loginname = loginname; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
创建UserService
package com.sxt.service; import com.sxt.domain.User; public interface UserService { /** * 根据用户名去查询用户对象 */ User queryUserByloginName(String loginname); }
创建UserServiceImpl
package com.sxt.service.impl; import com.sxt.domain.User; import com.sxt.service.UserService; public class UserServiceImpl implements UserService { @Override public User queryUserByloginName(String loginname) { User user = null; switch (loginname) { case "zhangsan": user = new User(1, "zhangsan", "张三", "123456"); break; case "lisi": user = new User(1, "lisi", "李四", "123456"); break; case "wangwu": user = new User(1, "wangwu", "王五", "123456"); break; } return user; } }
创建UserRealm
package com.sxt.realm; import com.sxt.domain.User; import com.sxt.service.UserService; import com.sxt.service.impl.UserServiceImpl; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.realm.AuthenticatingRealm; public class UserRealm extends AuthenticatingRealm { private UserService userService = new UserServiceImpl(); @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println(authenticationToken.getPrincipal() + "UserRealm"); //获取用户名 String username = authenticationToken.getPrincipal().toString(); //根据用户名查询用户对象 User user = userService.queryUserByloginName(username); if (null != user){ System.out.println("用户已存在"); // 创建这个对象返回之后会自动匹配密码 AuthenticationInfo info = new SimpleAuthenticationInfo("adc", user.getPassword(), "UserRealm"); return info; }else { return null;// 用户不存在 } } }
修改App.java进行测试
package com.sxt; import com.sxt.realm.UserRealm; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; /** * Hello world! */ public class App { public static void main( String[] args ) { String username = "zhangsan"; String password = "123456"; //创建SecurityManager工厂 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //配置自定义realm DefaultSecurityManager securityManager = (DefaultSecurityManager)factory.getInstance(); System.out.println(securityManager.getClass().getSimpleName()); //配置自定义realm securityManager.setRealm(new UserRealm()); //把当前的安全管理器绑定到当前线程 SecurityUtils.setSecurityManager(securityManager); //得到当前的subject Subject subject = SecurityUtils.getSubject(); //封装用户名和密码到token UsernamePasswordToken token = new UsernamePasswordToken(username, password); //进行登录 try { //进行认证,失败则抛异常 subject.login(token); }catch (AuthenticationException e){ System.out.println("用户名或密码正确"); } checkUserIsLogin(); } private static void checkUserIsLogin() { Subject subject = SecurityUtils.getSubject(); subject.isAuthenticated();//判断当前线程里面的Subject是否退出 System.out.println("是否认证通过:"+subject.isAuthenticated()); System.out.println("退出"); subject.logout();//退出 System.out.println("是否认证通过:"+subject.isAuthenticated()); } }
自定义Realm实现授权
为使用要使用自定义Realm实现授权
与上边认证自定义realm一样,大部分情况是要从数据库获取权限数据,这里直接实现基于资源的授权。
创建项目
修改pom.xml
<!--加入shiro的依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.5.0</version> </dependency> <!--日志包--> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
创建ActiverUser
package com.sxt.common; import com.sxt.domain.User; import java.util.List; public class ActiveUser { /** * 用户 */ private User user; /** * 角色 */ private List<String> roles; /** * 权限 */ private List<String> permissions; public ActiveUser() { } public ActiveUser(User user, List<String> roles, List<String> permissions) { this.user = user; this.roles = roles; this.permissions = permissions; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public List<String> getRoles() { return roles; } public void setRoles(List<String> roles) { this.roles = roles; } public List<String> getPermissions() { return permissions; } public void setPermissions(List<String> permissions) { this.permissions = permissions; } }
创建RoleService
package com.sxt.service; import java.util.List; public interface RoleService { List<String> queryRolesByLoginName(String loginname); }
创建RoleServiceImpl
package com.sxt.service.impl; import com.sxt.domain.User; import com.sxt.service.RoleService; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class RoleServiceImpl implements RoleService { @Override public List<String> queryRolesByLoginName(String loginname) { List<String> roles=new ArrayList<>(); User user=null; switch (loginname){ case "admin": roles.addAll(Arrays.asList("超级管理员","系统管理员","用户管理员","其它管理员")); break; case "zhangsan": roles.addAll(Arrays.asList("系统管理员")); break; case "lisi": roles.addAll(Arrays.asList("用户管理员","其它管理员")); break; case "wangwu": roles.addAll(Arrays.asList("系统管理员","用户管理员")); break; } return roles; } }
创建PermissionService
package com.sxt.service; import java.util.List; public interface PermissionService { List<String> queryPermissionsByLoginName(String loginname); }
创建PermissionServiceImpl
package com.sxt.service.impl; import com.sxt.domain.User; import com.sxt.service.PermissionService; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class PermissionServiceImpl implements PermissionService { @Override public List<String> queryPermissionsByLoginName(String loginname) { List<String> roles=new ArrayList<>(); User user=null; switch (loginname){ case "admin": roles.addAll(Arrays.asList("user:query","user:add","user:update","user:delete","user:export")); break; case "zhangsan": roles.addAll(Arrays.asList("user:query","user:add","user:update","user:delete")); break; case "lisi": roles.addAll(Arrays.asList("user:query","user:add","user:update","user:delete")); break; case "wangwu": roles.addAll(Arrays.asList("user:query")); break; } return roles; } }
创建UserRealm
package com.sxt.realm; import com.sxt.common.ActiveUser; import com.sxt.domain.User; import com.sxt.service.PermissionService; import com.sxt.service.RoleService; import com.sxt.service.UserService; import com.sxt.service.impl.PermissionServiceImpl; import com.sxt.service.impl.RoleServiceImpl; import com.sxt.service.impl.UserServiceImpl; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; 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 java.util.List; /** * 用户授权和验证 自定义realm */ public class UserRealm extends AuthorizingRealm { private UserService userService = new UserServiceImpl(); private RoleService roleSerivce = new RoleServiceImpl(); private PermissionService permissionSrvice = new PermissionServiceImpl(); public String getName(){ return this.getClass().getSimpleName(); } /** * 授权方法 只要调用 subject.hasRole** subject.isPermised**就会被回调 * @param principalCollection * @return 返回一个权限和角色的对象 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //创建返回对象 SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); //得到用户对象 ActiveUser activeUser = (ActiveUser) principalCollection.getPrimaryPrincipal(); List<String> roles = activeUser.getRoles(); List<String> permissions = activeUser.getPermissions(); //把当前用户拥有的角色和权限告诉shiro if(null!=roles&&roles.size()>0){ info.addRoles(roles); } if(null!=permissions&&permissions.size()>0){ info.addStringPermissions(permissions); } return info; } /** * 认证方法 * @param authenticationToken subject.login(toke)方法传过来的token对象 * @return 如果返回null 代表用户名不存在,返回不为null 说明用户名存在,返回之后在判断用户名是否正确 * @throws AuthenticationException 认证失败的异常 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println(authenticationToken.getPrincipal() + "UserRealm"); // 得到用户名 String username = authenticationToken.getPrincipal().toString(); // 根据用户登录名查询用户对象 User user = userService.queryUserByLoginName(username); if (user != null){ // 根据用户登录名取查询当前用户有哪些角色 List<String> roles = this.roleSerivce.queryRolesByLoginName(user.getLoginname()); // 根据用户登录名取查询当前用户有哪些权限 List<String> permissions = this.permissionSrvice.queryPermissionsByLoginName(user.getLoginname()); ActiveUser activeUser = new ActiveUser(user, roles, permissions); /** * 参数说明 * principal:向doGetAuthorizationInfo方法和subject.getPrincipal();返回一个对象 * credentials:用户密码的密文 * realmName:当前自定义Realem类的名称 我们这里是重写getName方法得到 */ AuthenticationInfo info = new SimpleAuthenticationInfo(activeUser, user.getPassword(), this.getName()); return info; }else { // 用户不存在 return null; } } }
创建测试类
package com.sxt; import com.sxt.realm.UserRealm; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; /** * Hello world! */ public class App { public static void main(String[] args) { String username = "zhangsan"; String password = "123456"; // 创建securityManager工厂 SecurityManager JDK也有这个类,在java.lang包 注意不要使用jdk里面那个类 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); // 从工厂里面得到一个serurityManager // SecurityManager securityManager = factory.getInstance(); // 配置自定义realm DefaultSecurityManager securityManager = (DefaultSecurityManager) factory.getInstance(); System.out.println(securityManager.getClass().getSimpleName()); // 配置自定义realm securityManager.setRealm(new UserRealm()); // 把当前的安全管理器绑定到当前线程 SecurityUtils.setSecurityManager(securityManager); // 得到当前的Object Subject subject = SecurityUtils.getSubject(); // 封装用户名和密码到token UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 进行登录 try { // 进行认证,如果失败会抛异常 subject.login(token); System.out.println("登录成功"); Object principal = subject.getPrincipal(); System.out.println(principal); // 到时候要写到登陆方法里面 可以把principal放到session里面 } catch (IncorrectCredentialsException e) { System.out.println("密码不正确"); } catch (UnknownAccountException e) { System.out.println("用户名不存在"); } catch (AuthenticationException e) { System.out.println("用户名或密码正确"); } if (subject.isAuthenticated()) { boolean hasRole = subject.hasRole("超级管理员"); System.out.println(username + "是否有超级管理员的角色:" + hasRole); //false boolean hasRole2 = subject.hasRole("系统管理员"); System.out.println(username + "是否有系统管理员的角色:" + hasRole2); //true // 权限 boolean permitted1 = subject.isPermitted("user:query"); System.out.println(username + "是否有user:query的权限:" + permitted1); //true boolean permitted2 = subject.isPermittedAll("user:query", "user:export"); System.out.println(username + "是否有同时拥有user:query和user:export的权限:" + permitted2); } else { System.out.println("未登录"); } } }
散列算法 + 凭证配置
散列算法
定义:对密码进行加密
散列算的分类
Md5
Sha1
工具类
package com.sxt.common; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.crypto.hash.Sha1Hash; public class MD5Utils { public static void main(String[] args) { System.out.println(md5("123456", "admin超级管理员", 2)); System.out.println(md5("123456", "zhangsan张三", 2)); // md5("123456","武汉",2); // sha1("123456","武汉",2); } private static String md5(String source, String salt, int hashiteraations) { Md5Hash hash1 = new Md5Hash("123456"); System.out.println("使用MD5加密一次:" + hash1); Md5Hash hash2 = new Md5Hash(hash1.toString()); // 参数说明 source 明文 salt 盐 就是一个加密的混淆字符串 Md5Hash hash3 = new Md5Hash("123456", "武汉"); System.out.println("使用MD5加密一次并加盐:" + hash3.toString()); Md5Hash hash4 = new Md5Hash("123456", "武汉", 3); System.out.println("使用SHA1加密3次并加盐:" + hash4.toString()); Md5Hash hash5 = new Md5Hash(source, salt, hashiteraations); return source; } private static void sha1(String source, String salt, int hashiteraations) { Sha1Hash hash1 = new Sha1Hash("123456"); System.out.println("使用SHA1加密一次:" + hash1); Sha1Hash hash2 = new Sha1Hash(hash1.toString()); System.out.println("使用SHA1加密二次:" + hash2); //参数说明 source 明文 salt 盐 就是一个加密的混淆字符串 Sha1Hash hash3 = new Sha1Hash("123456", "武汉"); System.out.println("使用SHA1加密一次并加盐:" + hash3.toString()); Sha1Hash hash4 = new Sha1Hash("123456", "武汉", 3); System.out.println("使用SHA1加密3次并加盐:" + hash4.toString()); } }
凭证配置
修改User
package com.sxt.domain; public class User { private Integer id; private String loginname; private String username; private String password; private Integer type; // 0:超级管理员 1:代表普通系统用户 public User() {} public User(Integer id, String loginname, String username, String password) { this.id = id; this.loginname = loginname; this.username = username; this.password = password; } public User(Integer id, String loginname, String username, String password, Integer type) { this.id = id; this.loginname = loginname; this.username = username; this.password = password; this.type = type; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLoginname() { return loginname; } public void setLoginname(String loginname) { this.loginname = loginname; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
修改UserServiceImpl
package com.sxt.service.impl; import com.sxt.domain.User; import com.sxt.service.UserService; public class UserServiceImpl implements UserService { @Override public User queryUserByloginName(String loginname) { User user=null; switch (loginname){ case "admin": user=new User(1, "admin", "超级管理员", "e0cb1490abfd76c2ffd5a63a531963b6", 0); break; case "zhangsan": user=new User(1, "zhangsan", "张三", "94e46f8fe8d948aab8943543e5da99e9", 1); break; case "lisi": user=new User(1, "lisi", "李四", "92932a67efcf212077659c69b989c460", 1); break; case "wangwu": user=new User(1, "wangwu", "王五", "2399a27720bfb00f20fbce035af38649", 1); break; } return user; } }
修改UserRealm
修改认证方法
/** * 参数说明 * 参数1:向doGetAuthorizationInfo方法和subject.getPrincipal();返回一个对象 * 参数2:数据库里面存放的加密的密文 * 参数3:盐 * 参数4:当前realm的名字 */ AuthenticationInfo info=new SimpleAuthenticationInfo(activeUser,user.getPassword(),credentialsSalt,this.getName());
测试并加入凭证匹配器
package com.sxt; import com.sxt.realm.UserRealm; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; /** * Hello world! */ public class App { public static void main(String[] args) { String username = "zhangsan"; String password = "123456"; // 创建securityManager工厂 SecurityManager JDK也有这个类,在java.lang包 注意不要使用jdk里面那个类 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); // 从工厂里面得到一个serurityManager // SecurityManager securityManager = factory.getInstance(); // 配置自定义realm DefaultSecurityManager securityManager = (DefaultSecurityManager) factory.getInstance(); System.out.println(securityManager.getClass().getSimpleName()); // 配置自定义realm UserRealm realm = new UserRealm(); // 设置凭证匹配器 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("md5");//加密方式 credentialsMatcher.setHashIterations(2); //散列次数 realm.setCredentialsMatcher(credentialsMatcher); securityManager.setRealm(realm); // 把当前的安全管理器绑定到当前线程 SecurityUtils.setSecurityManager(securityManager); // 得到当前的Object Subject subject = SecurityUtils.getSubject(); // 封装用户名和密码到token UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 进行登录 try { // 进行认证,如果失败会抛异常 subject.login(token); System.out.println("登录成功"); Object principal = subject.getPrincipal(); System.out.println(principal); // 到时候要写到登录方法里面 可以把principal放到session里面 } catch (IncorrectCredentialsException e) { System.out.println("密码不正确"); } catch (UnknownAccountException e) { System.out.println("用户名不存在"); } catch (AuthenticationException e) { System.out.println("用户名或密码正确"); } if (subject.isAuthenticated()) { boolean hasRole = subject.hasRole("超级管理员"); System.out.println(username + "是否有超级管理员的角色:" + hasRole);//false boolean hasRole2 = subject.hasRole("系统管理员"); System.out.println(username + "是否有系统管理员的角色:" + hasRole2);//true // 权限 boolean permitted1 = subject.isPermitted("user:query"); System.out.println(username + "是否有user:query的权限:" + permitted1);//true boolean permitted2 = subject.isPermittedAll("user:query", "user:export"); System.out.println(username + "是否有同时拥有user:query和user:export的权限:" + permitted2);// } else { System.out.println("未登录"); } } }
SSM+集成shiro+jsp
创建数据库【shiro】
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for permission -- ---------------------------- DROP TABLE IF EXISTS `permission`; CREATE TABLE `permission` ( `perid` int(11) NOT NULL AUTO_INCREMENT, `pername` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `percode` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`perid`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of permission -- ---------------------------- INSERT INTO `permission` VALUES (1, '用户查询', 'person:query'); INSERT INTO `permission` VALUES (2, '用户添加', 'person:add'); INSERT INTO `permission` VALUES (3, '用户修改', 'person:update'); INSERT INTO `permission` VALUES (4, '用户删除', 'person:delete'); INSERT INTO `permission` VALUES (5, '导出用户', 'person:export'); -- ---------------------------- -- Table structure for role -- ---------------------------- DROP TABLE IF EXISTS `role`; CREATE TABLE `role` ( `roleid` int(11) NOT NULL AUTO_INCREMENT, `rolename` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`roleid`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of role -- ---------------------------- INSERT INTO `role` VALUES (1, '超级管理员'); INSERT INTO `role` VALUES (2, 'CEO'); INSERT INTO `role` VALUES (3, '保安'); -- ---------------------------- -- Table structure for role_permission -- ---------------------------- DROP TABLE IF EXISTS `role_permission`; CREATE TABLE `role_permission` ( `perid` int(255) NULL DEFAULT NULL, `roleid` int(11) NULL DEFAULT NULL ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of role_permission -- ---------------------------- INSERT INTO `role_permission` VALUES (1, 1); INSERT INTO `role_permission` VALUES (2, 1); INSERT INTO `role_permission` VALUES (3, 1); INSERT INTO `role_permission` VALUES (4, 1); INSERT INTO `role_permission` VALUES (1, 2); INSERT INTO `role_permission` VALUES (2, 2); INSERT INTO `role_permission` VALUES (3, 2); INSERT INTO `role_permission` VALUES (1, 3); INSERT INTO `role_permission` VALUES (5, 3); -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `userid` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `userpwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `type` int(11) NULL DEFAULT 1 COMMENT '0:超级管理员 1:系统普通用户', PRIMARY KEY (`userid`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, 'zhangsan', '639ffb0cbcca39d4fff8348844b1974e', '男', '武汉', 1); INSERT INTO `user` VALUES (2, 'lisi', '0d303fa8e2e2ca98555f23a731a58dd9', '女', '北京', 1); INSERT INTO `user` VALUES (3, 'wangwu', '473c41db9af5cc0d90e7adfd2b6d9180', '女', '成都', 1); INSERT INTO `user` VALUES (4, 'admin', '65bba883c29e1e006306d2ff4db96b84', ' 男', '武汉', 0); -- ---------------------------- -- Table structure for user_role -- ---------------------------- DROP TABLE IF EXISTS `user_role`; CREATE TABLE `user_role` ( `userid` int(11) NULL DEFAULT NULL, `roleid` int(11) NULL DEFAULT NULL ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user_role -- ---------------------------- INSERT INTO `user_role` VALUES (1, 1); INSERT INTO `user_role` VALUES (2, 2); INSERT INTO `user_role` VALUES (3, 3); SET FOREIGN_KEY_CHECKS = 1;
创建项目
生成User
生成UserService
package com.sxt.service; import com.sxt.domain.User; public interface UserService{ /** * 根据登陆名查询用户对象 */ public User queryUserByUserName(String userName); }
生成UserServiceImpl
package com.sxt.service.impl; import com.sxt.domain.User; import org.springframework.stereotype.Service; import javax.annotation.Resource; import com.sxt.mapper.UserMapper; import com.sxt.service.UserService; @Service public class UserServiceImpl implements UserService{ @Resource private UserMapper userMapper; @Override public User queryUserByUserName(String userName) { return this.userMapper.queryUserByUserName(userName); } }
生成UserMapper
package com.sxt.mapper; import com.sxt.domain.User; public interface UserMapper { User queryUserByUserName(String userName); }
生成UserMapper.xml
<select id="queryUserByUserName" resultMap="BaseResultMap"> select * from user where username = #{value} </select>
生成Role
生成RoleService
package com.sxt.service; import java.util.List; public interface RoleService{ /** * 根据用户ID查询当前用户拥有的角色 * 在realm里面使用的List<String> */ List<String> queryRolesByUserId(Integer userId); }
生成RoleServiceImpl
package com.sxt.service.impl; import com.sxt.domain.Role; import org.springframework.stereotype.Service; import javax.annotation.Resource; import com.sxt.mapper.RoleMapper; import com.sxt.service.RoleService; import java.util.ArrayList; import java.util.List; @Service public class RoleServiceImpl implements RoleService{ @Resource private RoleMapper roleMapper; @Override public List<String> queryRolesByUserId(Integer userId) { List<Role> rolesList= this.roleMapper.queryRolesByUserId(userId); List<String> roles=new ArrayList<>(); for (Role role : rolesList) { roles.add(role.getRolename()); } return roles; } }
生成RoleMapper
package com.sxt.mapper; import com.sxt.domain.Role; import java.util.List; public interface RoleMapper { List<Role> queryRolesByUserId(Integer userId); }
生成RoleMapper.xml
<select id="queryRolesByUserId" resultMap="BaseResultMap"> select t1.* from role t1 inner join user_role t2 on (t1.roleid=t2.userid) where t2.userid = #{value} </select>
生成Permssion
生成PermssionService
package com.sxt.service; import java.util.List; public interface PermissionService{ /** * 根据用户ID查询当前用户拥有的角色 * 在realm里面使用的List<String> */ List<String> queryPermissionsByUserId(Integer userId); }
生成PermssionServiceImp
package com.sxt.service.impl; import com.sxt.domain.Permission; import org.springframework.stereotype.Service; import javax.annotation.Resource; import com.sxt.mapper.PermissionMapper; import com.sxt.service.PermissionService; import java.util.ArrayList; import java.util.List; @Service public class PermissionServiceImpl implements PermissionService{ @Resource private PermissionMapper permissionMapper; @Override public List<String> queryPermissionsByUserId(Integer userId) { List<Permission> permissionsList=this.permissionMapper.queryPermissionsByUserId(userId); List<String> permissions=new ArrayList<>(); for (Permission permission : permissionsList) { permissions.add(permission.getPercode()); } return permissions ; } }
生成PermssionMapper
<select id="queryPermissionsByUserId" resultMap="BaseResultMap"> select distinct t1.* from permission t1 inner join role_permission t2 inner join user_role t3 on(t1.perid=t2.perid and t2.roleid=t3.userid) where t3.userid=#{value} </select>
创建ActiverUser
package com.sxt.common; import com.sxt.domain.User; import java.util.List; public class ActiveUser { /** * 用户 */ private User user; /** * 角色 */ private List<String> roles; /** * 权限 */ private List<String> permissions; public ActiveUser(User user, List<String> roles, List<String> permissions) { this.user = user; this.roles = roles; this.permissions = permissions; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public List<String> getRoles() { return roles; } public void setRoles(List<String> roles) { this.roles = roles; } public List<String> getPermissions() { return permissions; } public void setPermissions(List<String> permissions) { this.permissions = permissions; } }
创建UserRealm
package com.sxt.realm; import com.sxt.common.ActiveUser; import com.sxt.domain.User; import com.sxt.service.PermissionService; import com.sxt.service.RoleService; import com.sxt.service.UserService; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; 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.apache.shiro.util.ByteSource; import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private PermissionService permissionService; @Override public String getName() { return this.getClass().getSimpleName(); } /** * 认证 * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //得到用户名 String username = authenticationToken.getPrincipal().toString(); //根据登陆名查询用户对象 User user = userService.queryUserByUserName(username); if(null != user){ // 根据用户登陆名去查询当前用户有哪些角色 List<String> roles = roleService.queryRolesByUserId(user.getUserid()); // 根据用户登陆名去查询当前用户有哪些权限 List<String> permissions = permissionService.queryPermissionsByUserId(user.getUserid()); ActiveUser activeUser = new ActiveUser(user, roles, permissions); /** * 参数说明 * principal:向doGetAuthorizationInfo方法和subject.getPrincipal();返回一个对象 * credentials:用户密码的密文 * realmName:当前自定义Realem类的名称 我们这里是重写getName方法得到 */ // AuthenticationInfo info = new SimpleAuthenticationInfo(activeUser, user.getPassword(), this.getName()); ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername() + user.getAddress()); //盐 /** * 参数说明 * 参数1:向doGetAuthorizationInfo方法和subject.getPrincipal();返回一个对象 * 参数2:数据库里面存放的加密的密文 * 参数3:盐 * 参数4:当前realm的名字 */ AuthenticationInfo info = new SimpleAuthenticationInfo(activeUser, user.getUserpwd(), credentialsSalt, this.getName()); return info; }else{ return null; //用户不存在 } } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); ActiveUser activeUser = (ActiveUser) principalCollection.getPrimaryPrincipal(); List<String> roles = activeUser.getRoles(); User user = activeUser.getUser(); List<String> permissions = activeUser.getPermissions(); if(user.getType() == 0){ // 超级管理员登陆 System.out.println("超级管理员"); info.addStringPermission("*:*"); }else { // 把当前用户拥有的角色和权限告诉shiro if(null != roles && roles.size() > 0){ info.addRoles(roles); } if(null != permissions && permissions.size() > 0){ info.addStringPermissions(permissions); } } return info; } }
修改pom.xml
引入相关包:
Spring、Springmvc、jackson、Mybatis、Pagehelper、Druid、Mysql、Log4j、文件上传
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sxt</groupId> <artifactId>05ssm</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>05ssm Maven Webapp</name> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <servlet.version>3.1.0</servlet.version> <jsp.version>2.3.1</jsp.version> <spring.version>4.3.24.RELEASE</spring.version> <jackson.version>2.10.0</jackson.version> <mybatis.version>3.5.4</mybatis.version> <mybatis-spring.version>2.0.3</mybatis-spring.version> <pagehelper.version>5.1.11</pagehelper.version> <mysql.version>8.0.19</mysql.version> <druid.version>1.1.21</druid.version> <fileupload.version>1.4</fileupload.version> <logging.version>1.2</logging.version> <log4j.version>1.2.17</log4j.version> <shiro.version>1.5.0</shiro.version> </properties> <dependencies> <!--servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${servlet.version}</version> <scope>provided</scope> </dependency> <!-- javax.servlet.jsp --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>${jsp.version}</version> <scope>provided</scope> </dependency> <!--spring的引入--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <!-- mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis-spring.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>${pagehelper.version}</version> </dependency> <!-- mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${fileupload.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>${logging.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <!--引入shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> </dependencies> <build> <finalName>05ssm</finalName> <plugins> <!-- 加入tomcat运行插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <!--解决页面提交数据乱码问题 --> <uriEncoding>UTF-8</uriEncoding> <!-- tomcat插件的请求端口 --> <port>8080</port> <!-- 项目的请求路径 --> <path>/ssm-shiro</path> </configuration> </plugin> </plugins> </build> </project>
创建db.properties
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/shiro?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username=root password=root initialSize=5 maxActive=10 minIdle=3
创建log4j.properties
# Global logging configuration log4j.rootLogger=DEBUG, stdout # MyBatis logging configuration... log4j.logger.org.mybatis.example.BlogMapper=TRACE # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
创建appliction-dao.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--加载db.properties--> <context:property-placeholder location="classpath*:db.properties" system-properties-mode="FALLBACK"/> <!--配置数据源--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"> <property name="driverClassName" value="${driverClassName}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> <property name="initialSize" value="${initialSize}"/> <property name="maxActive" value="${maxActive}"/> <property name="minIdle" value="${minIdle}"/> </bean> <!--声明配置对象--> <bean id="configuration" class="org.apache.ibatis.session.Configuration"> <!--在控制台输出sql--> <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/> </bean> <!--声明 sqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--注入数据源--> <property name="dataSource" ref="dataSource"/> <!--加入配置文件--> <!--<property name="configLocation" value="classpath*:mybatis.cfg.xml"></property>--> <property name="configuration" ref="configuration"/> <!--配置mppaer.xml的扫描--> <property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"/> <!--配置分页插件--> <property name="plugins"> <bean class="com.github.pagehelper.PageInterceptor"/> </property> </bean> <!--配置mapper接口的扫描--> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.sxt.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> </beans>
创建appliction-service.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 扫描service.impl --> <context:component-scan base-package="com.sxt.service.impl"/> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置事务的传播性 --> <tx:advice id="advice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="insert*" propagation="REQUIRED"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="del*" propagation="REQUIRED"/> <tx:method name="change*" propagation="REQUIRED"/> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="get*" read-only="true"/> <tx:method name="load*" read-only="true"/> <tx:method name="query*" read-only="true"/> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <!-- 声明切面 --> <aop:pointcut id="pc" expression="execution(* com.sxt.service.impl.*.*(..))"/> <!-- 织入 --> <aop:advisor advice-ref="advice" pointcut-ref="pc"/> </aop:config> </beans>
创建application-shiro.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 声明凭证匹配器 --> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!-- 散列算法名 --> <property name="hashAlgorithmName" value="md5"/> <!-- 散列次数 --> <property name="hashIterations" value="2"/> </bean> <!-- 创建realm --> <bean id="userRealm" class="com.sxt.realm.UserRealm"> <!-- 注入凭证匹配器 --> <property name="credentialsMatcher" ref="credentialsMatcher"/> </bean> <!-- 声明安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- 注入自定义realm --> <property name="realm" ref="userRealm"/> </bean> <!-- 声明过滤器 --> <!-- shiro的web过滤器, id必须和wen.xml里面的shiroFilter的targetBeanName的值一样 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 注入安全管理器 --> <property name="securityManager" ref="securityManager"/> <!-- 如果用户访问了需要认证的页面,而当前用户又没有认证时跳转的页面 --> <property name="loginUrl" value="/index.jsp"/> <!-- 用户登陆成功之后跳转的页面 (一般不用,因为我们是在Controller里面手动跳转的) --> <!-- <property name="successUrl" value="/success.jsp"/> --> <!-- 用户登录成功之后,访问没有权限的资源跳转的页面 --> <!-- <property name="unauthorizedUrl" value="/unauthorized.jsp"/> --> <!-- 配置过滤器资源 --> <property name="filterChainDefinitions"> <value> <!--配置放行的url--> /index.jsp*=anon /login/toLogin*=anon /login/doLogin*=anon <!--配置退出的url--> /login/logout*=logout <!--配置拦截的url--> /**=authc </value> </property> </bean> </beans>
创建applictionContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <import resource="classpath*:application-dao.xml"></import> <import resource="classpath*:application-service.xml"></import> <import resource="classpath*:application-shiro.xml"></import> </beans>
创建springmvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--扫描controller--> <context:component-scan base-package="com.sxt.controller"/> <!--配置映射器和适配器--> <mvc:annotation-driven/> <!--配置视图解析器的前后缀--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 注入前后缀 --> <property name="prefix" value="/WEB-INF/view/"/> <property name="suffix" value=".jsp"/> </bean> <!--配置文件上传--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8"/> <property name="maxUploadSize" value="20971520"/> <property name="uploadTempDir" value="/upload/temp"/> </bean> <!-- 配置静态文件放行 --> <mvc:default-servlet-handler/> </beans>
修改web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <!-- shiro拦截的过滤器 --> <filter> <filter-name>delegatingFilterProxy</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>targetBeanName</param-name> <!--必须和application-shrio里面的 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">保持一至--> <param-value>shiroFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>delegatingFilterProxy</filter-name> <servlet-name>springmvc</servlet-name> </filter-mapping> <!-- 配置编码过滤器 --> <filter> <filter-name>encFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encFilter</filter-name> <servlet-name>springmvc</servlet-name> </filter-mapping> <!-- 配置加载applicationContext 监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext.xml</param-value> </context-param> <!-- 配置前端控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- 配置durid的控制页面 --> <servlet> <servlet-name>duridServlet</servlet-name> <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class> <init-param> <param-name>loginUsername</param-name> <param-value>admin</param-value> </init-param> <init-param> <param-name>loginPassword</param-name> <param-value>123456</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>duridServlet</servlet-name> <url-pattern>/durid/*</url-pattern> </servlet-mapping> </web-app>
创建LoginController
package com.sxt.controller; import com.sxt.common.ActiveUser; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; 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.HttpServletRequest; @RequestMapping("login") @Controller public class LoginController { /** * 跳转到登陆页面 * @return */ @RequestMapping("toLogin") public String toLogin() { return "login"; } /** * 登陆方法 * @param username 用户名 * @param password 密码 * @param request * @return */ @RequestMapping("doLogin") public String doLogin(String username, String password, HttpServletRequest request) { UsernamePasswordToken token = new UsernamePasswordToken(username, password); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); ActiveUser activeUser = (ActiveUser) subject.getPrincipal(); request.getSession().setAttribute("username", activeUser.getUser().getUsername()); return "list"; } catch (AuthenticationException e) { request.setAttribute("error", "用户名或密码不正确"); return "login"; } } }
创建index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <jsp:forward page="/login/toLogin.do"/> </body> </html>
创建WEB-INF/view/login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>用户登陆</title> </head> <body> <h1 align="center">用户登陆</h1> <h3 align="center" style="color: red">${error}</h3> <form id="dataFrom" action="login/doLogin.do" method="post" > <table align="center" cellpadding="5" cellspacing="5" border="2"> <tr> <td>登陆名:</td> <td> <input type="text" name="username"> </td> </tr> <tr> <td>密码:</td> <td> <input type="password" name="password"> </td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="提交"> </td> </tr> </table> </form> </body> </html>
创建WEB-INF/view/list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> <html> <head> <title>列表</title> </head> <body> <shiro:authenticated>认证通过</shiro:authenticated> <shiro:hasRole name="超级管理员">当前用户是超级管理</shiro:hasRole> <shiro:hasPermission name="person:query"> <h3>查询</h3> </shiro:hasPermission> <shiro:hasPermission name="person:add"> <h3>添加</h3> </shiro:hasPermission> <shiro:hasPermission name="person:update"> <h3>修改</h3> </shiro:hasPermission> <shiro:hasPermission name="person:delete"> <h3>删除</h3> </shiro:hasPermission> <shiro:hasPermission name="person:export"> <h3>导出</h3> </shiro:hasPermission> </body> </html>
SSM+集成shiro+记住我
复制05ssm的项目
修改WEB-INF/view/login.jsp
修改LoginContrller
创建PersonContrller
package com.sxt.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("person") public class PersonController { /** * 跳转到WEB-inf/view/list.jsp */ @RequestMapping("toList") public String toList() { return "list"; } }
修改application-shiro.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--声明凭证匹配器--> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!--散列算法名--> <property name="hashAlgorithmName" value="md5"/> <!--散列次数--> <property name="hashIterations" value="2"/> </bean> <!--创建realm--> <bean id="userRealm" class="com.sxt.realm.UserRealm"> <!--注入凭证匹配器--> <property name="credentialsMatcher" ref="credentialsMatcher"/> </bean> <!--声明cookie--> <bean id="cookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <!--设置cookie的名称 相当于Cookie: JSESSIONID=5B11D1E9C30684F5528F377D67963CE7 的JSESSIONID--> <constructor-arg name="name" value="rememberMe"/> <!-- 只有http请求时才能使用cookie --> <property name="httpOnly" value="true"/> <!-- 设置cookie的存活时间 7天 (单位:秒) --> <property name="maxAge" value="604800"/> </bean> <!--声明记住我的管理器--> <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cookie" ref="cookie"/> </bean> <!--声明安全管理器--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--注入自定义realm--> <property name="realm" ref="userRealm"/> <!--注入记住我的管理器--> <property name="rememberMeManager" ref="rememberMeManager"/> </bean> <!--声明过滤器--> <!-- Shiro 的Web过滤器 id必须和web.xml里面的shiroFilter的 targetBeanName的值一样 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!--注入安全管理器--> <property name="securityManager" ref="securityManager"/> <!--如果用户访问需要认证的页面,而当前用户又没有认证时跳转的页面--> <property name="loginUrl" value="/index.jsp"/> <!--如果用户登陆成功之后跳转的页面 一般不用,因为我们是在Controller里面手动跳转的--> <!--<property name="successUrl" value="/success.jsp"></property>--> <!--如果用户登陆成功之后 访问没有授权的资源 就跳转到这个页面--> <!--<property name="unauthorizedUrl" value="/unauthorized.jsp"></property>--> <!--注入自定义过滤器--> <property name="filters"> <map> <entry key="rememberMe"> <bean class="com.sxt.filter.RememberMeFilter"> </bean> </entry> </map> </property> <!--配置过滤器资源--> <property name="filterChainDefinitions"> <value> <!--配置放行的url--> /index.jsp*=anon /login/toLogin*=anon /login/doLogin*=anon <!--配置退出的url--> /login/logout*=logout <!--配置拦截的url--> /**=rememberMe,user /*=authc /*/*=authc </value> </property> </bean> </beans>
测试
先以记住我的方式登陆,成功之后再关闭浏览器,再请求http://127.0.0.1:8080/ssm-shiro/person/toList.do发现不用认证了,再清空cookie再请求一个要认证的地址 ,发现要认证。
以上存在丢失session的问题
解决丢失session的问题
创建RememberMeFilter
package com.sxt.filter; import com.sxt.common.ActiveUser; import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class RememberMeFilter extends FormAuthenticationFilter { @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { // 得到主体 Subject subject = this.getSubject(request, response); // 从主体里面得到Session Session session = subject.getSession(); // 判断session里面有没有username Object username = session.getAttribute("username"); if (null == username) { // 在从主体里面取出身份ActiveUser ActiveUser activeUser = (ActiveUser) subject.getPrincipal(); if (null != activeUser) { session.setAttribute("username", activeUser.getUser().getUsername()); } } return true; // true:代表放行 false:代表没人认证 } }
修改application-shiro.xml的配置
ssm+shrio实现前后端分离
复制05ssm项目的内容到07ssmShrioAjax这个项目
修改pom.xml
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency>
修改LoginController
package com.sxt.controller; import com.sxt.common.ActiveUser; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; 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.HttpServletRequest; import java.util.HashMap; import java.util.Map; @RequestMapping("login") @Controller public class LoginController { // 跳转到登陆页面 @RequestMapping("toLogin") public String toLogin(){ return "login"; } // 登陆方法 @RequestMapping("doLogin") public Map<String, Object> doLogin(String username, String password, HttpServletRequest request){ Map<String,Object> map = new HashMap<>(); UsernamePasswordToken token=new UsernamePasswordToken(username,password); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); ActiveUser activeUser = (ActiveUser) subject.getPrincipal(); map.put("code", 200); map.put("msg", "用户登录成功"); map.put("currentUser", activeUser.getUser()); return map; }catch (AuthenticationException e){ map.put("code", -1); map.put("msg","用户名或密码不正确"); return map; } } }
创建PersonController
package com.sxt.controller; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresUser; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("person") public class PersonController { /** * 要有user:query的权限才能调用 * @return */ @RequiresPermissions(value = {"person:query"}) @RequestMapping("queryAllPerson") public Map<String,Object> queryAllPerson(){ Map<String,Object> map = new HashMap<>(); map.put("msg","person:query"); return map; } /** * 修改 * @return */ @RequiresPermissions(value = {"person:update"}) @RequestMapping("updatePerson") public Map<String,Object> updatePerson(){ Map<String,Object> map = new HashMap<>(); map.put("msg","person:update"); return map; } /** * 添加 * @return */ @RequiresPermissions(value = {"person:add"}) @RequestMapping("addPerson") public Map<String,Object> addPerson(){ Map<String,Object> map = new HashMap<>(); map.put("msg","person:add"); return map; } /** * 删除 * @return */ @RequiresPermissions(value = {"person:delete"}) @RequestMapping("deletePerson") public Map<String,Object> deletePerson(){ Map<String,Object> map = new HashMap<>(); map.put("msg","person:delete"); return map; } /** * 导出 * @return */ @RequiresPermissions(value = {"person:export"}) @RequestMapping("exportPerson") public Map<String,Object> exportPerson(){ Map<String,Object> map = new HashMap<>(); map.put("msg","person:export"); return map; } }
以zhangsan登陆出现personExport方法还能调用
zhangsan登陆出现personExport方法还能调用的问题
因为我们现在使用的注解方法
@RequiresPermissions(value = {"user:delete"}) 代表当前方法被调用时当前用户必有user:delete的权限
@RequiresRoles(value = "普通管理员") 代表当前方法被调用时当前用户必有普通管理员的的角色
启动shiro的注解
修改springmvc.xml
测试以zhangsan登陆
发现persion:query person:add person:update person:delete可以调用
发现person:export 不能调用 出现未授权的异常
处理授权的异常 以json串的形式返回出去 使用Springmvc的全异常
创建GlobExceptionAspect
package com.sxt.common; import org.apache.shiro.authz.UnauthorizedException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.HashMap; import java.util.Map; @RestControllerAdvice // 如果出现异常,会返回一个json字符串 //@ControllerAdvice //如果出现异常,会跳转到某个页面 public class GlobExceptionAspect { @ExceptionHandler(UnauthorizedException.class) public Map<String, Object> unAuthorized(){ Map<String, Object> map = new HashMap<>(); map.put("code", 302); map.put("msg","没有调用权限"); return map; } }
扫描异常类
zhangsan登陆清空session之后返回未登陆的处理
重写authc的过滤器,修改ShiroLoginFilter
package com.sxt.filter; import com.alibaba.fastjson.JSON; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class ShiroLoginFilter extends FormAuthenticationFilter { /** * 在访问controller前判断是否登录,返回json,不进行重定向。 * @param request * @param response * @return true-继续往下执行,false-该filter过滤器已经处理,不继续执行其他过滤器 * @throws Exception */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException { HttpServletResponse httpServletResponse = (HttpServletResponse) response; //if (isAjax(request)) { httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("application/json"); Map<String,Object> resultData = new HashMap<>(); resultData.put("code", -1); resultData.put("msg", "登录认证失效,请重新登录!"); httpServletResponse.getWriter().write(JSON.toJSON(resultData).toString()); /* } else { // saveRequestAndRedirectToLogin(request, response); // @Mark 非ajax请求重定向为登录页面 httpServletResponse.sendRedirect("/login"); }*/ return false; } private boolean isAjax(ServletRequest request) { String header = ((HttpServletRequest) request).getHeader("X-Requested-With"); if ("XMLHttpRequest".equalsIgnoreCase(header)) { return Boolean.TRUE; } return Boolean.FALSE; } }
修改application-shiro.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 声明凭证匹配器 --> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!-- 散列算法名 --> <property name="hashAlgorithmName" value="md5"/> <!-- 散列次数 --> <property name="hashIterations" value="2"/> </bean> <!-- 创建realm --> <bean id="userRealm" class="com.sxt.realm.UserRealm"> <!-- 注入凭证匹配器 --> <property name="credentialsMatcher" ref="credentialsMatcher"/> </bean> <!-- 声明安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- 注入自定义realm --> <property name="realm" ref="userRealm"/> </bean> <!-- 声明过滤器 --> <!-- shiro的web过滤器, id必须和wen.xml里面的shiroFilter的targetBeanName的值一样 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 注入安全管理器 --> <property name="securityManager" ref="securityManager"/> <!-- 如果用户访问了需要认证的页面,而当前用户又没有认证时跳转的页面 --> <!-- <property name="loginUrl" value="/index.jsp"/> --> <!-- 用户登陆成功之后跳转的页面 (一般不用,因为我们是在Controller里面手动跳转的) --> <!-- <property name="successUrl" value="/success.jsp"/> --> <!-- 用户登录成功之后,访问没有权限的资源跳转的页面 --> <!-- <property name="unauthorizedUrl" value="/unauthorized.jsp"/> --> <!-- 重写authc的过滤器 --> <property name="filters"> <map> <entry key="authc"> <bean class="com.sxt.filter.ShiroLoginFilter"> </bean> </entry> </map> </property> <!-- 配置过滤器资源 --> <property name="filterChainDefinitions"> <value> <!--配置放行的url--> /login/doLogin*=anon <!--配置退出的url--> /login/logout*=logout <!--配置拦截的url--> /**=authc </value> </property> </bean> </beans>
测试方法
以张三登陆,访问http://127.0.0.1:8080/bjsxt/person/exportPerson.do 可以访问,但是调用权限。
清空浏览器缓存
访问http://127.0.0.1:8080/bjsxt/person/exportPerson.do 可以访问,但是返回的没有登陆。
来源:https://www.cnblogs.com/lyang-a/p/12569400.html