shiro-Authorization

What is authorization?

Authorization is access control, which controls whether a user has permission to do something in the application.

Three elements of authorization:

permissions, roles, users

 

3. Three authorization methods of Shiro

3.1 Encoding method authorization

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);

    /**
     * Get identity information, we can get the user's permissions and role information from the database in this method
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        log.info("----------doGetAuthorizationInfo method is called----------");
        String username = (String) getAvailablePrincipal(principals);
        //Get permission string from database by username
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // permission
        Set<String> s = new HashSet<String>();
        s.add("printer:print");
        s.add("printer:query");
        info.setStringPermissions(s);
        //Role
        Set<String> r = new HashSet<String>();
        r.add("role1");
        info.setRoles(r);

        return info;
    }
    /**
     * In this method, perform authentication
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        //username
        String username = (String) token.getPrincipal();
        log.info("username:"+username);
        //password
        String password = new String((char[])token.getCredentials());
        log.info("password:"+password);
        //Get the username and password from the database for matching, here for the sake of aspect, the database operation is omitted
        if(!"admin".equals(username)){
            throw new UnknownAccountException();
        }
        if(!"123".equals(password)){
            throw new IncorrectCredentialsException();
        }
        //Authentication passed, return an identity information
        AuthenticationInfo aInfo = new SimpleAuthenticationInfo(username,password,getName());

        return aInfo;
    }

}

 

  • Configuration file (shiro-realm.ini)
#declare a realm  
MyRealm1=com.shiro.realm.MyRealm1
#Specify the realms implementation of securityManager  
securityManager.realms=$MyRealm1

 

  • Verify permissions and roles
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) {
        //Get an instance of SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");
        SecurityManager securityManager = factory.getInstance();

        SecurityUtils.setSecurityManager(securityManager);

        Subject currenUser = SecurityUtils.getSubject();

        //if not authenticated
        if(!currenUser.isAuthenticated()){
            UsernamePasswordToken token = new UsernamePasswordToken("admin","123");
            token.setRememberMe(true);
            try {
                currenUser.login(token);
            } catch (UnknownAccountException uae) {
                log.info("No such user: " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                log.info( token.getPrincipal() + "Incorrect password!");
            } catch (LockedAccountException lae) {
                log.info( token.getPrincipal() + "locked, please contact administrator");
            }catch (AuthenticationException ae) {
                //other unknown exception
            }
        }

        if(currenUser.isAuthenticated())
            log.info("User"+currenUser.getPrincipal() +"Login successful");
        //======================Using coding to verify permissions and roles====================
        //Is there a role of role1
        if(currenUser.hasRole("role1")){
            log.info("has role role1");
        }else{
            log.info("No role role1");
        }
        //Do you have permission to print to the printer?
        if(currenUser.isPermitted("printer:print")){
            log.info("Can print to the printer");
        }else {
            log.info("Cannot print to printer");
        }
    }

}

 

  • In addition to the hasRolesum used above isPermitted, there are other APIs that can verify authorization

------------------Role-----------------

method returns
hasRole(String roleName) Returns true if the Subject is assigned the specified role
hasRoles(List<String> roleNames) Returns an array of hasRole results consistent with the directory in the method argument
hasAllRoles(Collection<String> roleNames) Returns true if the Subject is assigned all roles
checkRole(String roleName) On success, no value is returned, and the program continues to execute; on failure, an exception message will be thrown
checkRoles(Collection<String> roleNames) On success, no value is returned, and the program continues to execute; on failure, an exception message will be thrown
checkRole(String… roleNames) On success, no value is returned, and the program continues to execute; on failure, an exception message will be thrown

————————————————————Permissions——————————————————–

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

 

3.2 Annotation-based authorization

Annotation-based authorization requires AOP support, we use spring here

 

  • Create a java project ( note that it is not a web project, there are some differences in the configuration of the two)

  • Customize Realm (this step is exactly the same as MyRealm1 above, just make a copy)

  • Create a spring configuration file (applicationContext.xml) under src
<?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();
        //The method is called normally when there is permission, otherwise an exception is thrown
        helloAnno.testAnnotation();
    }
}

 

When there is permission, it is called normally, when there is no permission, the result is as follows 

 

 

  • and other notes

Annotation
@RequiresAuthentication

Require that the current Subject has been authenticated in the current session

can only be accessed or called by the annotated class/instance/method

@RequiresGues

Requires the current Subject to be a "guest", i.e. they must be

Annotated classes/instances/methods can be accessed or invoked without being validated or remembered in the previous session

@RequiresPermissions

Requires that the current Subject be allowed one or more permissions in order to execute the annotated method,

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

@RequiresRoles

Requires the current Subject to have all specified roles. If they don't, the method will not be executed,

And AuthorizationException will be thrown. For example: @RequiresRoles("administrator")

@RequiresUser

Requires the current Subject to be an application user in order to be accessed or invoked by the annotated class/instance/method.

Either confirmed by authentication, or remembered by the 'RememberMe' service in the previous session

 

3.3 JSP tag authorization

 

1. The above uses an ordinary java project combined with spring to authorize the annotation method, and the jsp tag authorization must use the web project. 
2. We use springmvc+spring+shiro this time

 

 

  • Create a web project
  • Create HelloAnno.java and MyRealm1.java (the same as above, just copy)
  • Create springmvc.xml under src

 

<?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页面应该打印出有查询和打印两个权限,我们来看看结果是否与我们预期

 

 

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326134968&siteId=291194637