赞
踩
一。 shiro简介
Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。
Shiro为解决下列问题(我喜欢称它们为应用安全的四要素)提供了保护应用的API:
从2003年至今,框架选择方面的情况已经改变了不少,但今天仍有令人信服的理由让你选择Shiro。其实理由相当多,Apache Shiro:
核心概念:Subject,SecurityManager和Realms
Subject
Subject一词是一个安全术语,其基本意思是“当前的操作用户”。称之为“用户”并不准确,因为“用户”一词通常跟人相关。在安全领域,术语“Subject”可以是人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。在代码的任何地方,你都能轻易的获得Shiro Subject,
SecurityManager
Subject的“幕后”推手是SecurityManager。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。它是Shiro框架的核心,充当“保护伞”,引用了多个内部嵌套安全组件,它们形成了对象图。但是,一旦SecurityManager及其内部对象图配置好,它就会退居幕后,应用开发人员几乎把他们的所有时间都花在Subject API调用上。
那么,如何设置SecurityManager呢?嗯,这要看应用的环境。例如,Web应用通常会在Web.xml中指定一个Shiro Servlet Filter,这会创建SecurityManager实例,如果你运行的是一个独立应用,你需要用其他配置方式,但有很多配置选项。
一个应用几乎总是只有一个SecurityManager实例。它实际是应用的Singleton(尽管不必是一个静态Singleton)。跟Shiro里的几乎所有组件一样,SecurityManager的缺省实现是POJO,而且可用POJO兼容的任何配置机制进行配置 - 普通的Java代码、Spring XML、YAML、.properties和.ini文件等。基本来讲,能够实例化类和调用JavaBean兼容方法的任何配置形式都可使用。
为此,Shiro借助基于文本的INI配置提供了一个缺省的“公共”解决方案。INI易于阅读、使用简单并且需要极少依赖。你还能看到,只要简单地理解对象导航,INI可被有效地用于配置像SecurityManager那样简单的对象图。注意,Shiro还支持Spring XML配置及其他方式,但这里只我们只讨论INI。
Realms
Shiro的第三个也是最后一个概念是Realm。Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当切实与像用户帐户这类安全相关数据进行交互,执行认证(登录)和授权(访问控制)时,Shiro会从应用配置的Realm中查找很多内容。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现
文字摘自 (http://www.infoq.com/cn/articles/apache-shiro)
shiro架构
除前面所讲Subject、SecurityManager 、Realm三个核心组件外,Shiro主要组件还包括:
常用的操作
ini配置权限信息(参考http://shiro.apache.org/configuration.html)
maven项目添加支持
# ======================= # Shiro INI configuration # ======================= [main] [users] # 设置用户信息 # 语法是 username = password, roleName1, roleName2, …, roleNameN jiaozi = 123456,role1 [roles] # 角色信息和角色拥有的权限 #语法是 rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN #权限的语法 * 表示所有权限 一般语法是 权限类型.权限动作.权限的资源id 比如 user:delete:1 表示拥有删除1号用户的权限 user:delete:*表示删除所有用户权限 admin = * role1 = user:query:*, user:delete:1 [urls] # web中的url过滤
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-core</artifactId>
- <version>1.4.0</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- </dependency>
测试验证权限过程(参考http://shiro.apache.org/10-minute-tutorial.html)
- package shiro;
-
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.authc.UsernamePasswordToken;
- import org.apache.shiro.config.IniSecurityManagerFactory;
- import org.apache.shiro.session.Session;
- import org.apache.shiro.subject.Subject;
- import org.apache.shiro.util.Factory;
-
- public class TestShiro {
- public static void main(String[] args) {
- testIni();
- }
- public static void testIni() {
- //从配置文件中读取用户的权限信息
- Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
- org.apache.shiro.mgt.SecurityManager securityManager = (org.apache.shiro.mgt.SecurityManager)factory.getInstance();
- SecurityUtils.setSecurityManager(securityManager);
- //获取登录用户信息
- Subject currentUser = SecurityUtils.getSubject();
- Session session = currentUser.getSession();
- session.setAttribute( "保存数据的键", "保存数据的值" );
- /**
- * 用户包括两部分
- // * principals and credentials
- * principals(本人)表示用户的标识信息 比如用户名 用户地址等
- * credentials(凭证)表示用户用于登录的凭证 比如密码 证书等
- */
- //isAuthenticated判断是否登录过
- if ( !currentUser.isAuthenticated() ) {
- //令牌 用户名和密码 其实就是credentials
- UsernamePasswordToken token = new UsernamePasswordToken("jiaozi", "123456");
- token.setRememberMe(true);
- //开始登录操作 操作后 isAuthenticated就是true
- currentUser.login(token);
- System.out.println("登录成功");
- //判断是否有某个角色
- if(currentUser.hasRole("role1")){
- System.out.println("拥有角色role1");
- }
- if(currentUser.isPermitted("user:query:1")){
- System.out.println("拥有查询1号用户权限");
- }
- }
-
- currentUser.logout();
-
- }
-
- }
一般权限信息存在于是数据库 我这里模拟放在内存变量map中
添加realm的实现类
- package shiro;
-
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Map;
- import java.util.Set;
-
- import org.apache.shiro.authc.AuthenticationException;
- import org.apache.shiro.authc.AuthenticationInfo;
- import org.apache.shiro.authc.AuthenticationToken;
- import org.apache.shiro.authc.SimpleAccount;
- import org.apache.shiro.authc.UsernamePasswordToken;
- import org.apache.shiro.authz.AuthorizationInfo;
- import org.apache.shiro.authz.SimpleAuthorizationInfo;
- import org.apache.shiro.realm.AuthorizingRealm;
- import org.apache.shiro.subject.PrincipalCollection;
- /**
- * 自定义realm的实现
- * @author jiaozi
- *
- */
- public class MyRealm extends AuthorizingRealm {
- //用于存放用户信息
- static Map<String,String> userList=null;
- //用于存放角色信息
- static Map<String,String> roleList=null;
- //每个realm都有一个名字
- static String REALM_NAME="myrealm";
- static{
- //这里也可以从数据库读取
- //模拟用户
- userList=new HashMap();
- userList.put("zs", "123456,role2,role3");
- //模拟权限
- roleList=new HashMap();
- roleList.put("role2","user:query:*");
- roleList.put("role3", "user:*");
- }
- /**
- * 支持哪种令牌
- */
- @Override
- public boolean supports(AuthenticationToken token) {
- // TODO Auto-generated method stub
- return token instanceof UsernamePasswordToken;
- }
- /**
- * 获取权限过程
- */
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
- //获取用户名
- String userName=principals.getPrimaryPrincipal().toString();
- //构建权限的类
- SimpleAuthorizationInfo sai=new SimpleAuthorizationInfo();
- Set<String> proleList=new HashSet<String>();
- Set<String> stringPermissions=new HashSet<String>();
- if(userList.containsKey(userName)){
- String[] roles=userList.get(userName).toString().split(",");
- for(int i=1;i<roles.length;i++){
- proleList.add(roles[i]);
- String pp=roleList.get(roles[i]);
- String[] ppArry=pp.split(",");
- for(int j=0;j<ppArry.length;j++){
- stringPermissions.add(ppArry[j]);
- }
- }
- }
- sai.setRoles(proleList);
- sai.setStringPermissions(stringPermissions);
- return sai;
- }
- /**
- * 认证过程
- */
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
- UsernamePasswordToken upt=(UsernamePasswordToken)token;
- String userName=token.getPrincipal().toString();
- String password=String.valueOf(upt.getPassword());
- if(userList.containsKey(userName)){
- String realPwd=userList.get(userName).toString().split(",")[0];
- if(realPwd.equals(password)){
- SimpleAccount sa=new SimpleAccount(userName,password,"REALM_NAME");
- return sa;
- }
- }
- return null;
- }
-
- }
ini配置文件中配置该realm main的部分添加
- [main]
- # 定义securityManager, Realms and 其他
- myRealm= shiro.MyRealm
- securityManager.realms=$iniRealm , $myRealm
- #iniRealm是内置的默认realm读取ini文件的传入 如果自定义了realm该realm被替代 如果还想使用可以直接引用
- #如果有多个realm 策略是所有的realm都要验证通过 还是只需要一个验证通过 我这里只需要一个验证通过 默认是所有
- #参考http://shiro.apache.org/authentication.html
- authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
- securityManager.authenticator.authenticationStrategy = $authcStrategy
同上测试 只要ini或者自定义ream存在的用户都能登录
二。 shiro web
集成web集成 参考 http://shiro.apache.org/web.html#programmatic-support
在之前例子基础上进行拓展 添加war项目 添加shiro-web依赖
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-web</artifactId>
- <version>1.4.0</version>
- </dependency>
web.xml添加shiro支持的过滤器和ini文件路径配置参数
- <context-param>
- <param-name>shiroConfigLocations</param-name>
- <param-value>/WEB-INF/shiro.ini</param-value>
- </context-param>
- <listener>
- <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
- </listener>
- <filter>
- <filter-name>ShiroFilter</filter-name>
- <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>ShiroFilter</filter-name>
- <url-pattern>/*</url-pattern>
- <dispatcher>REQUEST</dispatcher>
- <dispatcher>FORWARD</dispatcher>
- <dispatcher>INCLUDE</dispatcher>
- <dispatcher>ERROR</dispatcher>
- </filter-mapping>
添加几个html和jsp用于测试
login.html添加用于登录的表单
- <form action="loginServlet" method="post">
- 用戶名 :<input type="text" name="userName"/>
- 密碼:<input type="text" name="password"/>
- <input type="submit">
- </form>
添加query.jsp用于当登录成功后跳转的页面
- <body>
- 這是測試查詢頁面
- </body>
添加add.jsp用于测试拥有某个角色才能进入的页面
- <body>
- 這是測試新增頁面
- </body>
添加一个检测没有角色失败页面 un.jsp
- <body>
- 没有权限
- </body
t添加一个登录的servlet
- <servlet>
- <description></description>
- <display-name>LoginServlet</display-name>
- <servlet-name>LoginServlet</servlet-name>
- <servlet-class>cn.et.web.LoginServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>LoginServlet</servlet-name>
- <url-pattern>/loginServlet</url-pattern>
- </servlet-mapping>
servlet类
修改shiro.ini添加路径过滤
package cn.et.web; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * 登录进入方法 */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken upt=new UsernamePasswordToken(request.getParameter("userName"), request.getParameter("password")); try { subject.login(upt); request.getRequestDispatcher("/query.jsp").forward(request, response); } catch (AuthenticationException e) { request.getRequestDispatcher("/login.html").forward(request, response); } } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
测试不登录直接访问 query.jsp或者add.jsp直接跳转到login.html中
# ======================= # Shiro INI configuration # ======================= [main] # 定义securityManager, Realms and 其他 myRealm= shiro.MyRealm securityManager.realms=$iniRealm , $myRealm #如果有多个realm 策略是所有的realm都要验证通过 还是只需要一个验证通过 我这里只需要一个验证通过 默认是所有 authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy securityManager.authenticator.authenticationStrategy = $authcStrategy authc.loginUrl = /login.html #如果沒有登錄 會先跳轉到loginUrl 如果登錄了沒權限 跳轉到unauthorizedUrl指定頁面 roles.loginUrl= /login.html roles.unauthorizedUrl= /un.html [users] # 设置用户信息 # 语法是 username = password, roleName1, roleName2, …, roleNameN jiaozi = 123456,role1 [roles] # 角色信息和角色拥有的权限 #语法是 rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN #权限的语法 * 表示所有权限 一般语法是 权限类型.权限动作.权限的资源id 比如 user:delete:1 表示拥有删除1号用户的权限 user:delete:*表示删除所有用户权限 admin = * role1 = user:query:*, user:delete:1 [urls] # web中的url过滤 #語法是 某個路徑 = 怎麼樣過濾1,過濾2 常用的過濾有 # anon 匿名用戶 # authc 表示用戶和密碼驗證過濾 類 org.apache.shiro.web.filter.authc.FormAuthenticationFilter 沒有登錄自動跳轉到登錄頁 # perms 是否擁有某些權限過濾 類 org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 用法 perms["remote:invoke"] # roles是否擁有某個角色 org.apache.shiro.web.filter.authz.RolesAuthorizationFilter 用法roles[administrator] # user 是否是某個用戶 org.apache.shiro.web.filter.authc.UserFilter # 也可以在main中自定義filter url就可以應用 參考http://shiro.apache.org/web.html#programmatic-support /login.html = anon /loginServlet = anon /query.jsp = authc /add.jsp = roles[role2]
测试登录自定义relam的zs用户 发现都可以进入
shiro通过自定义过滤器 定制自己的过滤规则 比如url信息全部位于数据库 需要所有的除了控制层的路径都经过自定义的url过滤可以添加类 继承自AuthorizationFilter实现isAccessAllowed方法
在main中定义该filter 直接在url中引用即可
package cn.et.conf; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.shiro.web.filter.authz.AuthorizationFilter; public class MyFilter extends AuthorizationFilter { /* * 返回true表示允许访问 false表示不允许访问 */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { return false; } }
- myfilter=cn.et.conf.MyFilter
- /** myfilter
当用户访问了某个页面 通过authc过滤跳转到登录页面 当用户登录后 希望自动跳转到之前的页面 如果是直接进入的登录页就进入默认的页面 的实现过程
偶尔 有些无法跳转 结果下载了fav.ico 因为有时一个请求到页面 会顺带一个获取图标的请求 如果图标请求在后面 shiro记录的前面的途径就是这个图标的请求 此时在html添加 禁止网页图标的请求
<link rel="icon" href="data:image/ico;base64,aWNv">
登录实现
@RequestMapping("loginBlog") public String login(String userName,String password,HttpServletRequest request){ Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken upt=new UsernamePasswordToken(userName,password); try { subject.login(upt); subject.getSession().setAttribute("userInfo", userDao.queryByContent(userName, password).get(0)); //关键代码 shiro自动保存了上一次请求的地址 只需要获取并重新跳转即可 //可以使用WebUtils.issueRedirect重定向跳转 SavedRequest savedRequest = WebUtils.getSavedRequest(request); if(savedRequest!=null) return "redirect:"+savedRequest.getRequestURI()+"?"+savedRequest.getQueryString(); return "/queryBlog"; } catch (AuthenticationException e) { return "redirect:/login.html"; } }
三。 shiro集成springboot
参考官网 spring集成 只是换成springboot (http://shiro.apache.org/spring.html)
添加maven的jsp支持和shiro-spring和springboot
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>1.5.9.RELEASE</version>
- </parent>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.tomcat.embed</groupId>
- <artifactId>tomcat-embed-jasper</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-core</artifactId>
- <version>1.4.0</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-web</artifactId>
- <version>1.4.0</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-spring</artifactId>
- <version>1.4.0</version>
- </dependency>
-
- <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- </dependency>
-
- </dependencies>
html和realm还是之前的 ini文件被替换成在spring中配置 添加配置的bean
- package cn.et.boot;
-
- import java.util.HashMap;
- import java.util.LinkedHashSet;
- import java.util.Map;
-
- import org.apache.shiro.realm.Realm;
- import org.apache.shiro.spring.LifecycleBeanPostProcessor;
- import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
- import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.web.servlet.FilterRegistrationBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.filter.DelegatingFilterProxy;
-
- import shiro.MyRealm;
-
- @Configuration
- public class Config {
- /**
- * 等價于 web.xml配置
- * 自動 將 /*的請求 委託給spring容器中 bean名字和filter-name一致的bean處理
- *
- <filter>
- <filter-name>shiroFilter</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>
- </filter>
- <filter-mapping>
- <filter-name>shiroFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- * @return
- */
- @Bean
- public FilterRegistrationBean webShiroFilter(){
- FilterRegistrationBean frb=new FilterRegistrationBean();
- DelegatingFilterProxy dfp=new DelegatingFilterProxy();
- frb.setFilter(dfp);
- frb.setName("shiroFilter");
- LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();
- linkedHashSet.add("/*");
- frb.setUrlPatterns(linkedHashSet);
-
-
- Map<String, String> initParameters=new HashMap<String, String>();
- initParameters.put("targetFilterLifecycle", "true");
- frb.setInitParameters(initParameters);
- return frb;
- }
- /**
- * 配置我的realm
- * @return
- */
- @Bean
- public Realm myRealm(){
- return new MyRealm();
- }
- /**
- * 定義默認的securityManager
- * @return
- */
- @Bean
- public DefaultWebSecurityManager securityManager(@Autowired Realm myRealm){
- DefaultWebSecurityManager dwm=new DefaultWebSecurityManager();
- dwm.setRealm(myRealm);
- return dwm;
- }
-
- /**
- * 定義和過濾器一致名字的ShiroFilterFactoryBean
- */
- @Bean
- public ShiroFilterFactoryBean shiroFilter(@Autowired org.apache.shiro.mgt.SecurityManager securityManager){
- ShiroFilterFactoryBean sffb=new ShiroFilterFactoryBean();
- sffb.setSecurityManager(securityManager);
- sffb.setLoginUrl("/login.html");
- sffb.setUnauthorizedUrl("/un.jsp");
- Map<String, String> urls=new HashMap<String, String>();
- /*
- * 定义url
- /login.html = anon
- /loginServlet = anon
- /query.jsp = authc
- /add.jsp = roles[role2]
- * */
-
- urls.put("/login.html", "anon");
- urls.put("/loginServlet", "anon");
- urls.put("/query.jsp", "authc");
- urls.put("/add.jsp", "roles[role1]");
- sffb.setFilterChainDefinitionMap(urls);
- return sffb;
- }
- /**
- * 定義後置處理器
- * @return
- */
- @Bean
- public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
- return new LifecycleBeanPostProcessor();
- }
- }
将之前servlet替换成springmvc的controller
添加一个springboot启动类 just run
package cn.et.boot; 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; @Controller public class LoginController { @RequestMapping("loginServlet") public String login(String userName,String password){ Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken upt=new UsernamePasswordToken(userName,password); try { subject.login(upt); return "/query.jsp"; } catch (AuthenticationException e) { return "redirect:/login.html"; } } }
- package cn.et.boot;
-
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
-
- @SpringBootApplication
- public class Main {
-
- public static void main(String[] args) {
- SpringApplication.run(Main.class, args);
- }
-
- }
通过 http://localhost:8080/login.html 访问
四。自定义filter实现动态配置url拦截
如果在spring中配置静态的url拦截 添加一个新的路径 每次都需要修改配置文件 如果使用springboot 每次都需要修改urls这个map 这个map是初始化bean
的时候创建 后面再往里面加是没有效果的 所以需要在拦截时自己手工调用对应的filter逻辑
自定义过滤器的代码是 注意添加到spring容器中 添加@Component注解
@Bean public ShiroFilterFactoryBean shiroFilter(@Autowired org.apache.shiro.mgt.SecurityManager securityManager){ ShiroFilterFactoryBean sffb=new ShiroFilterFactoryBean(); sffb.setSecurityManager(securityManager); sffb.setLoginUrl("/login.html"); sffb.setUnauthorizedUrl("/un.jsp"); Map<String, String> urls=new HashMap<String, String>(); /* * 定义url /login.html = anon /loginServlet = anon /query.jsp = authc /add.jsp = roles[role2] * */ urls.put("/login.html", "anon"); urls.put("/loginServlet", "anon"); urls.put("/query.jsp", "authc"); urls.put("/add.jsp", "roles[role1]"); sffb.setFilterChainDefinitionMap(urls); return sffb; }
- package cn.et.conf;
-
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Set;
- import java.util.regex.Pattern;
-
- import javax.servlet.Filter;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
-
- import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
- import org.apache.shiro.subject.Subject;
- import org.apache.shiro.util.CollectionUtils;
- import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
- import org.apache.shiro.web.filter.authz.AuthorizationFilter;
- import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
- import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
- @Component
- public class MyFilter extends AuthorizationFilter {
- static Map<String, String> urls=new HashMap<String, String>();
-
- @Autowired
- private ShiroFilterFactoryBean sffb;
- /**
- * 匹配指定过滤器规则的url
- * @param regex
- * @param url
- * @return
- */
- public static boolean matchUrl(String regex,String url){
- regex=regex.replaceAll("/+", "/");
- if(regex.equals(url)){
- return true;
- }
- regex=regex.replaceAll("\\.", "\\\\.");
- // /login.html /l*.html
- regex=regex.replaceAll("\\*", ".*");
- // /**/login.html /a/b/login.html
- if(regex.indexOf("/.*.*/")>=0){
- regex=regex.replaceAll("/\\.\\*\\.\\*/", "((/.*/)+|/)");
- }
- System.out.println(regex+"----"+url);
- return Pattern.matches(regex, url);
- }
- /**
- * 测试
- * @param args
- */
- public static void main(String[] args) {
- System.out.println(matchUrl("/**/s*.html","/t/g/login.html"));
- }
- /**
- * 在map中模拟 这个也可以将来定义在数据库中
- */
- static{
- urls.put("/login.html", "anon");
- urls.put("/loginBlog", "anon");
- urls.put("/un.jsp", "anon");
- urls.put("/queryBlogByToken", "anon");
- urls.put("/query.jsp", "authc");
- urls.put("/add.jsp", "roles[role1]");
- }
- /**
- * isAccessAllowed用于判断当前url的请求是否能验证通过 如果验证失败 调用父类的onAccessDenied决定跳转到登录失败页还是授权失败页面
- */
- @Override
- protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
- throws Exception {
- HttpServletRequest req=(HttpServletRequest)request;
- String url=req.getRequestURI();
- //通过url获取授权类型
- String urlAuth=urls.get(url);
- if(urlAuth==null){
- return false;
- }
- //配置的过滤器是anon 直接放过
- if(urlAuth.startsWith("anon")){
- return true;
- }
- //配置的是authc 判断当前用户是否认证通过
- Subject subject = getSubject(request, response);
- if(urlAuth.startsWith("authc")){
- return subject.isAuthenticated();
- }
- //授权认证 也需要判断是否登录 没有登录返回 登录继续下面的验证
- boolean ifAuthc=subject.isAuthenticated();
- if(!ifAuthc)
- return ifAuthc;
- //如果是定义的roles过滤器 获取所有的roles 一般是roles[a,b]
- if(urlAuth.startsWith("roles")){
- String[] rolesArray=urlAuth.split("roles\\[")[1].split("\\]")[0].split(",");
- if (rolesArray == null || rolesArray.length == 0) {
- return true;
- }
- Set<String> roles = CollectionUtils.asSet(rolesArray);
- return subject.hasAllRoles(roles);
- }
- if(urlAuth.startsWith("perms")){
- String[] perms=urlAuth.split("perms\\[")[1].split("\\]")[0].split(",");
- boolean isPermitted = true;
- if (perms != null && perms.length > 0) {
- if (perms.length == 1) {
- if (!subject.isPermitted(perms[0])) {
- isPermitted = false;
- }
- } else {
- if (!subject.isPermittedAll(perms)) {
- isPermitted = false;
- }
- }
- }
-
- return isPermitted;
- }
- return false;
- }
-
- }
修改springboot的Config类 将所有的请求都交给自定义的过滤器处理
- @Bean
- public ShiroFilterFactoryBean shiroFilter(@Autowired org.apache.shiro.mgt.SecurityManager securityManager){
- ShiroFilterFactoryBean sffb=new ShiroFilterFactoryBean();
- sffb.setSecurityManager(securityManager);
- sffb.setLoginUrl("/login.html");
- sffb.setUnauthorizedUrl("/un.jsp");
- Map<String, String> urls=new HashMap<String, String>();
- urls.put("/**", "myFilter");
- sffb.setFilterChainDefinitionMap(urls);
- return sffb;
- }
添加一个过滤匹配的策略 定义多个relms 只要有一个realm的数据通过则通过 (这里只有一个realm不配置也可 留着做记录 将来怕要用)
/** * 多个relms 只要有一个realm的数据通过则通过 * @return */ @Bean public ModularRealmAuthenticator authentictor(){ org.apache.shiro.authc.pam.ModularRealmAuthenticator mra=new org.apache.shiro.authc.pam.ModularRealmAuthenticator(); mra.setAuthenticationStrategy(new org.apache.shiro.authc.pam.FirstSuccessfulStrategy()); return mra; } /** * 定義默認的securityManager * @return */ @Bean public DefaultWebSecurityManager securityManager(@Autowired Realm myRealm,@Autowired ModularRealmAuthenticator authentictor){ DefaultWebSecurityManager dwm=new DefaultWebSecurityManager(); dwm.setRealm(myRealm); authentictor.setRealms(dwm.getRealms()); dwm.setAuthenticator(authentictor); return dwm; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。