MyException - 我的异常网
当前位置:我的异常网» 行业应用 » shiro-受权(Authorization)

shiro-受权(Authorization)

www.MyException.Cn  网友分享于:2013-11-16  浏览:0次
shiro-授权(Authorization)

什么是授权?

授权,就是访问控制,控制某个用户在应用程序中是否有权限做某件事.

授权三要素:

权限、角色、用户

 

3.Shiro的三种授权方式

3.1 编码方式授权

package com.shiro.realm;

import java.util.HashSet;
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.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
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.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyRealm1 extends AuthorizingRealm{

    private static final transient Logger log = LoggerFactory.getLogger(Main.class);

    /**
     * 获取身份信息,我们可以在这个方法中,从数据库获取该用户的权限和角色信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        log.info("----------doGetAuthorizationInfo方法被调用----------");
        String username = (String) getAvailablePrincipal(principals);
        //通过用户名从数据库获取权限字符串
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //权限
        Set<String> s = new HashSet<String>();
        s.add("printer:print");
        s.add("printer:query");
        info.setStringPermissions(s);
        //角色
        Set<String> r = new HashSet<String>();
        r.add("role1");
        info.setRoles(r);

        return info;
    }
    /**
     * 在这个方法中,进行身份验证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        //用户名
        String username = (String) token.getPrincipal();
        log.info("username:"+username);
        //密码
        String password = new String((char[])token.getCredentials());
        log.info("password:"+password);
        //从数据库获取用户名密码进行匹配,这里为了方面,省略数据库操作
        if(!"admin".equals(username)){
            throw new UnknownAccountException();
        }
        if(!"123".equals(password)){
            throw new IncorrectCredentialsException();
        }
        //身份验证通过,返回一个身份信息
        AuthenticationInfo aInfo = new SimpleAuthenticationInfo(username,password,getName());

        return aInfo;
    }

}

 

  • 配置文件(shiro-realm.ini)
#声明一个realm  
MyRealm1=com.shiro.realm.MyRealm1 
#指定securityManager的realms实现  
securityManager.realms=$MyRealm1

 

  • 验证权限和角色
package com.shiro.realm;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {

    private static final transient Logger log = LoggerFactory.getLogger(Main.class);

    public static void main(String[] args) {
        //获取SecurityManager的实例
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");
        SecurityManager securityManager = factory.getInstance();

        SecurityUtils.setSecurityManager(securityManager);

        Subject currenUser = SecurityUtils.getSubject();

        //如果还未认证
        if(!currenUser.isAuthenticated()){
            UsernamePasswordToken token = new UsernamePasswordToken("admin","123");
            token.setRememberMe(true);
            try {
                currenUser.login(token);
            } catch (UnknownAccountException uae) {
                log.info("没有该用户: " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                log.info( token.getPrincipal() + " 的密码不正确!");
            } catch (LockedAccountException lae) {
                log.info( token.getPrincipal() + " 被锁定 ,请联系管理员");
            }catch (AuthenticationException ae) {
                //其他未知的异常
            }
        }

        if(currenUser.isAuthenticated())
            log.info("用户 "+currenUser.getPrincipal() +" 登录成功");
        //=====================使用编码方式进行权限和角色的验证==================
        //是否有role1这个角色
        if(currenUser.hasRole("role1")){
            log.info("有角色role1");
        }else{
            log.info("没有角色role1");
        }
        //是否有对打印机进行打印操作的权限
        if(currenUser.isPermitted("printer:print")){
            log.info("可以对打印机进行打印操作");
        }else {
            log.info("不可以对打印机进行打印操作");
        }
    }

}

 

  • 除了上面用到的hasRoleisPermitted,还有其他api可以验证授权

—————————————————-角色————————————————–

方法 返回
hasRole(String roleName) 返回true 如果Subject 被分配了指定的角色
hasRoles(List<String> roleNames) 返回一个与方法参数中目录一致的hasRole结果的数组
hasAllRoles(Collection<String> roleNames) 返回true 如果Subject 被分配了所有的角色
checkRole(String roleName) 成功,不返回任何值,程序继续执行;失败时,将抛出异常信息
checkRoles(Collection<String> roleNames) 成功,不返回任何值,程序继续执行;失败时,将抛出异常信息
checkRole(String… roleNames) 成功,不返回任何值,程序继续执行;失败时,将抛出异常信息

—————————————————-权限————————————————–

方法
isPermitted(String perm)
isPermitted(String… perms)
checkPermission(Permission p)
checkPermissions(Collection<Permission> perms)

 

3.2 基于注解的授权

基于注解的授权,需要有AOP的支持,我们这里使用spring

 

  • 创建一个java工程(注意,不是web工程,两者的配置有一些差别)

  • 自定义Realm(这一步和上面的MyRealm1一模一样,复制一份即可)

  • 在src下创建spring配置文件(applicationContext.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"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.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-4.0.xsd">

    <context:component-scan base-package="com.shiro.anno"></context:component-scan>

    <!-- 配置Realm -->
    <bean id="MyRealm1" class="com.shiro.anno.MyRealm1"></bean>

    <!-- 配置securityManager -->
    <bean id="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
        <property name="realm" ref="MyRealm1" />
    </bean>
    <!-- 生命周期 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

    <!-- 启用shiro注解 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
    <bean
        class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!-- 
        让securityManager这个bean成为静态单例的bean
        注意:在web应用中,不要配置这个
    -->
    <bean
        class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod"
            value="org.apache.shiro.SecurityUtils.setSecurityManager" />
        <property name="arguments" ref="securityManager" />
    </bean>

</beans>

 

  • 写一个类用于注解授权
package com.shiro.anno;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Service;

@Service
public class HelloAnno {

    public void login(){

        Subject currenUser = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("admin","123");
        token.setRememberMe(true);
        currenUser.login(token);
    }
    /**
     * 有printer:print权限才能调用该方法
     * 否则抛异常
     */
    @RequiresPermissions({"printer:print"})
    public void testAnnotation(){
        System.out.println("使用注解方式。。。");
    }
}

 

  • 测试
package com.shiro.anno;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void main(String[] args) {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloAnno helloAnno = (HelloAnno)ctx.getBean("helloAnno");

        helloAnno.login();
        //有权限时,该方法才正常调用,否则抛异常
        helloAnno.testAnnotation();
    }
}

 

当有权限时,正常调用,没有权限时,结果如下 

 

 

  • 还有其他的注解

注解 说明
@RequiresAuthentication

要求当前Subject 已经在当前的session 中被验证通过

才能被注解的类/实例/方法访问或调用

@RequiresGues

要求当前的Subject 是一个“guest”,也就是他们必须是

在之前的session中没有被验证或记住才能被注解的类/实例/方法访问或调用

@RequiresPermissions

要求当前的Subject 被允许一个或多个权限,以便执行注解的方法,

比如:@RequiresPermissions(“account:create”)

@RequiresRoles

要求当前的Subject 拥有所有指定的角色。如果他们没有,则该方法将不会被执行,

而且AuthorizationException 异常将会被抛出。比如:@RequiresRoles(“administrator”)

@RequiresUser

需要当前的Subject 是一个应用程序用户才能被注解的类/实例/方法访问或调用。

要么是通过验证被确认,或者在之前session 中的’RememberMe’服务被记住

 

3.3 JSP标签授权

 

1.上面使用的是普通的java工程结合spring进行注解方式的授权,jsp标签授权就必须使用web项目了。 
2.我们这次使用springmvc+spring+shiro

 

 

  • 创建web项目
  • 创建HelloAnno.java和MyRealm1.java(和上面一模一样,复制即可)
  • 在src下创建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-4.0.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-4.0.xsd">

    <context:component-scan base-package="com.shiro.controller"></context:component-scan>

    <mvc:annotation-driven></mvc:annotation-driven>
    <mvc:default-servlet-handler/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
        <property name="prefix" value="/WEB-INF/views/"></property>  
        <property name="suffix" value=".jsp"></property>  
    </bean>  

</beans>

 

 

  • 在src下创建applicationContext.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"
        xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.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-4.0.xsd">
    
        <context:component-scan base-package="com.shiro.annotation"></context:component-scan>
    
        <!-- shiro过滤器bean,id要和web.xml中filter-name一致 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager" />
    
            <property name="filterChainDefinitions">
                <value>
                    #这里相当于ini配置文件中的[urls]
                    #url=拦截器[参数],拦截器
                    # some example chain definitions:
                    /admin/** = authc, roles[admin]
                    /docs/** = authc, perms[document:read]
                    # 当访问login时,不用进行认证(anon表示匿名)
                    /login = anon
                    /** = authc
                    # more URL-to-FilterChain definitions here
                </value>
            </property>
        </bean>
    
        <!-- 配置securityManager -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
            <property name="realm" ref="myRealm" />
            <!-- <property name="sessionMode" value="native"/> -->
        </bean>
        <!-- 生命周期 -->
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
    
        <!-- 配置Realm: -->
        <bean id="myRealm" class="com.shiro.annotation.MyRealm1"></bean>
    
        <!-- 启用shiro注解 -->
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
            <property name="securityManager" ref="securityManager"/>
        </bean>
    
        <!-- ============一般情况还要配置 数据源,事务 等等=============== -->
    </beans>
     其实在spring配置文件中,只使用到了spring的IOC容器,其他的配置都是在配置shiro
  • web.xml中配置,让spring,springmvc,shiro过滤器起作用
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>shiro2</display-name>



  <!-- name要和 applicationContext.xml中的对应的bean的id一致 -->
    <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> 

    <!-- 配置启动 Spring IOC 容器的 Listener -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!-- Bootstraps the root web application context before servlet initialization -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- springmvc.xml -->
    <servlet>  
        <servlet-name>dispatcherServlet</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>dispatcherServlet</servlet-name>  
        <url-pattern>/</url-pattern>  
      </servlet-mapping> 

</web-app>

 

  • 在web目录下创建login.jsp登录界面
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1>login</h1>
    <form action="login">
        <label>username:</label>
        <input type="text" name="username"/>
        <label>password:</label>
        <input type="text" name="password"/>
        <input type="submit" value="submit"/>
    </form>
</body>
</html>

 

  • 对应的处理登录请求的controller
package com.shiro.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.shiro.annotation.HelloAnno;

@Controller
public class TestController {

    @Autowired
    private HelloAnno helloAnno;

    @RequestMapping("/login")
    public String test(String username,String password){
        System.out.println("username:"+username);
        System.out.println("password"+password);

        //登录
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject currentUser = SecurityUtils.getSubject();
        //如果登录失败,会抛异常,应该要捕捉异常
        currentUser.login(token);

        if(currentUser.isAuthenticated()){
            System.out.println("认证成功");
            //有权限才能调用该方法,没权限将抛异常
            helloAnno.testAnnotation();
        }

        return "success";
    }
}

 

  • 在WEB-INF/views下创建success.jsp(要导入标签库)
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1>successful</h1>
    <!-- 如果当前用户有printer:print权限,标签内的内容才显示 -->
    <shiro:hasPermission name="printer:print">
        我有打印机的打印权限
    </shiro:hasPermission>
    <shiro:hasPermission name="printer:query">
        我有打印机的查询权限
    </shiro:hasPermission>
    <shiro:hasPermission name="printer:delete">
        我有打印机的删除权限
    </shiro:hasPermission>
</body>
</html>

 

在我们自定义的Realm中,我们给了该用户两个权限(printer:print和printer:query),因此success.jsp页面应该打印出有查询和打印两个权限,我们来看看结果是否与我们预期

 

 

 

 

 

文章评论

当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
程序员和编码员之间的区别
程序员和编码员之间的区别
每天工作4小时的程序员
每天工作4小时的程序员
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序员都该阅读的书
程序员都该阅读的书
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序员的鄙视链
程序员的鄙视链
鲜为人知的编程真相
鲜为人知的编程真相
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
编程语言是女人
编程语言是女人
 程序员的样子
程序员的样子
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
漫画:程序员的工作
漫画:程序员的工作
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
一个程序员的时间管理
一个程序员的时间管理
老程序员的下场
老程序员的下场
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
我的丈夫是个程序员
我的丈夫是个程序员
程序员应该关注的一些事儿
程序员应该关注的一些事儿
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
如何成为一名黑客
如何成为一名黑客
程序员必看的十大电影
程序员必看的十大电影
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
旅行,写作,编程
旅行,写作,编程
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
Java程序员必看电影
Java程序员必看电影
那些争议最大的编程观点
那些争议最大的编程观点
代码女神横空出世
代码女神横空出世
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有