Shiro——授权

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fancheng614/article/details/83477345

在实际项目中,每一个用户角色登录进系统看到的菜单可能会不一样,这样就涉及到了用户权限的问题,Shiro也是目前常用的权限框架。

源码下载

一、授权方式

shiro支持三种授权方式:

1、编程式(基本不用):通过写if/else授权代码块完成。

2、注解式:通过在执行的Java方法上防止相应的注解完成,没有权限将抛出相应的异常。

3、JSP标签:在JSP页面通过相应的标签完成。

二、默认拦截器

1、rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。

2、port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。

3、perms:例子/admins/user/**=perms[user:add:*],perms参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

4、roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如/admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。

5、anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

6、authc:例如/admins/user/**=authc表示需要认证才能使用,没有参数

7、authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证

8、ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

9、user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查

这些过滤器分为两组,一组是认证过滤器,一组是授权过滤器。其中anon,authcBasic,auchc,user是第一组,

perms,roles,ssl,rest,port是第二组。

三、Shiro标签

使用shiro标签之前要在jsp中引入标签库
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>

shiro的几种标签:
shiro:guest:用户没有身份验证时显示的信息 ,即游客访问信息
shiro:user:用户登陆后显示的信息,通过RememberMe认证通过
shiro:principal:显示用户身份信息
shiro:authenticated:用户已经身份验证通过,即通过Subject.login登录成功,不是RememberMe登录的
shiro:notAuthenticated:用户未进行身份验证,即没有通过Subject.login登录,包括RememberMe自动登录的也属于未进行身份验证
shiro:hasRole name="admin":如果当前Subject有角色,将显示body体内容
shiro:hasAnyRoles name="admin,user":只要有body体内有任意一个角色,就会显示里面的内容
shiro:lacksRole name="admin":如果当前的Subject没有角色将显示标签内内容 
shiro:hasPermission name="user:create":如果当前Subject有权限就显示标签里的内容 
shiro:lacksPermission name="org:create":如果当前Subject没有权限将显示标签里的内容

四、权限注解

权限注解可以用在service和controller层
1、@RequiresAuthentication:表示当前Subject已经通过login进行了身份验证;即Subject.isAuthenticated()返回true
2、@RequiresUser:表示当前Subject已经身份验证或者通过RemmberMe登录的
3、@RequiresGuest:表示当前Subject没有身份验证或通过RemmberMe登录过,即游客身份
4、@RequiresRole(value={"admin","user"},logical=Logical.AND):表示当前Subject需要角色admin和Uuser
5、RequiresPermissions(value={"user:a","user:b"},logical=Logical.OR):表示当前Subject需要权限user:a或user:b

五、具体实现

1、在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>shiro-2</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <!-- 1、配置Spring环境 -->
  <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <!-- 2、配置SpringMVC环境 -->
  <servlet>
  	<servlet-name>spring</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>classpath:spring-servlet.xml</param-value>
    </init-param>
  	<load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>spring</servlet-name>
  	<url-pattern>/</url-pattern>
  </servlet-mapping>
  
  <!-- 
  3、配置shiro 的 shiroFilter
  DelegatingFilterProxy实际上是Filter的一个代理对象,默认情况下。
  Spring会到IOC容器中查找和<filter-name>相应的filter bean。
      也可以通过targetBeanName的初始化参数来配置filter 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>
</web-app>

2、配置springmvc配置文件:spring-servlet.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/mvc 
	http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
	
	<!-- 1、配置springmvc注解扫描 -->
	<context:component-scan base-package="com.mfc"></context:component-scan>
	
	<!-- 2、启用springmvc注解 -->
	<mvc:annotation-driven></mvc:annotation-driven>
	<mvc:default-servlet-handler/>
	
	<!-- 3、 配置springmvc访问前后缀 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
	
</beans>

3、配置IOC容器: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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
	
    <!-- 
    	1、配置securityManager
     -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="authenticator" ref="authenticator"/>
        <property name="realms">
        	<list>
    			<ref bean="jdbcRealm"/>
    			<ref bean="secondRealm"/>
    		</list>
        </property>
        <?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-4.0.xsd">
	
    <!-- 
    	1、配置securityManager
     -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="authenticator" ref="authenticator"/>
        <property name="realms">
        	<list>
    			<ref bean="jdbcRealm"/>
    			<ref bean="secondRealm"/>
    		</list>
        </property>
        
        <!-- 设置RemmberMe失效时间,注意这一句代码是后来加的,在下载的资源里面没有 -->
        <property name="rememberMeManager.cookie.maxAge" value="10"></property>
    </bean>

    <!-- 
    	2、配置cacheManager
    		2.1、需要加入ehcache的jar包及配置文件
     -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
    </bean>

    <!-- 
    	3、配置Realm    
    		3.1、直接配置实现了org.apache.shiro.realm.Realm接口的bean
     -->
    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
    	<!-- 
    	配置认证策略 :
    	"FirstSuccessfulStrategy":只要有一个Realm验证成功即可,其他Realm忽略,只返回第一个验证陈宫的Realm身份验证成功的认证信息
    	"AtLeastOneSuccessfulStrategy":只要有一个Realm验证成功即可,返回所有Realm身份验证成功的认证信息
    	"AllSuccessfulStrategy":所有的Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败了就算是失败了
    	-->
    	<property name="authenticationStrategy">
    		<bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>
    	</property>
    </bean>
    <bean id="jdbcRealm" class="com.mfc.realm.ShiroRealm">
    	<property name="credentialsMatcher">
    		<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    			<property name="hashAlgorithmName" value="MD5"></property>
    			<property name="hashIterations" value="1024"></property>
    		</bean>
    	</property>
    </bean>
    <bean id="secondRealm" class="com.mfc.realm.SecondRealm">
    	<property name="credentialsMatcher">
    		<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    			<property name="hashAlgorithmName" value="SHA1"></property>
    			<property name="hashIterations" value="1024"></property>
    		</bean>
    	</property>
    </bean>

    <!-- 
    4、配置lifecycleBeanPostProcessor,可以自动的来调用配置在Spring IOC 容器中的shiro bean的生命周期方法
     -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- 
    5、启用IOC容器中使用shiro的注解。但必须在配置了lifecycleBeanPostProcessor之后才可以使用
     -->
    <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>

    <!-- 
    6、配置shiroFilter
    	6.1、id必须和web.xml中的配置的DelegatingFilterProxy的<filter-name>的值一致
     		若不一致会抛出异常NoSuchBeanDefinitionException。因为shiro会来IOC容器中查找和<filter-name>名字对应的filter bean
     -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.jsp"/>
        <property name="successUrl" value="/list.jsp"/>
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
        
        <!-- 可以从数据表中初始化资源和权限,代替下面使用 filterChainDefinitions写死的权限配置-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
        <!-- 
        	URL匹配模式:
        	?:匹配一个字符。如/admin将匹配/admin1,但不匹配/admin或/admin/
        	*:匹配0个或多个字符串。如/admin*将匹配/admin、/admin123,但不匹配/admin/1
        	**:匹配路径中的零个或多个路径。如/admin/**将匹配/admin/a或admin/a/b
        
        	配置哪些页面需要受保护,以及访问这些页面需要的权限,这里的URL权限采取第一次匹配优先的方式
        	1、anon 可以被匿名访问
        	2、authc 必须认证(即登录)后才可以访问的页面
        	3、logout 注销
        	4、roles 角色过滤器
         -->
         <!-- 
        <property name="filterChainDefinitions">
            <value>
                /login.jsp = anon
                /shiroCtrl/login = anon
                /shiroCtrl/logout = logout
                
                /user.jsp = roles[user]
                /admin.jsp = roles[admin]
                # everything else requires authentication:
                /** = authc
            </value>
        </property>
         -->
    </bean>
	
	<!--配置一个bean,该bean实际上是一个map,通过实例工厂方法的方式  -->
	<bean id="filterChainDefinitionMap"
		factory-bean="filterChainDefinitionMapBuilder"
		factory-method="buildFilterChainDefinitionMapBuilder"></bean>
	<bean id="filterChainDefinitionMapBuilder"
		class="com.mfc.factory.FilterChainDefinitionMapBuilder"></bean>
		
    <!-- 注入测试注解配置权限的service  -->
	<bean id="shiroService" class="com.mfc.service.ShiroService"></bean>
</beans>
    </bean>

    <!-- 
    	2、配置cacheManager
    		2.1、需要加入ehcache的jar包及配置文件
     -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
    </bean>

    <!-- 
    	3、配置Realm    
    		3.1、直接配置实现了org.apache.shiro.realm.Realm接口的bean
     -->
    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
    	<!-- 
    	配置认证策略 :
    	"FirstSuccessfulStrategy":只要有一个Realm验证成功即可,其他Realm忽略,只返回第一个验证陈宫的Realm身份验证成功的认证信息
    	"AtLeastOneSuccessfulStrategy":只要有一个Realm验证成功即可,返回所有Realm身份验证成功的认证信息
    	"AllSuccessfulStrategy":所有的Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败了就算是失败了
    	-->
    	<property name="authenticationStrategy">
    		<bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>
    	</property>
    </bean>
    <bean id="jdbcRealm" class="com.mfc.realm.ShiroRealm">
    	<property name="credentialsMatcher">
    		<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    			<property name="hashAlgorithmName" value="MD5"></property>
    			<property name="hashIterations" value="1024"></property>
    		</bean>
    	</property>
    </bean>
    <bean id="secondRealm" class="com.mfc.realm.SecondRealm">
    	<property name="credentialsMatcher">
    		<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    			<property name="hashAlgorithmName" value="SHA1"></property>
    			<property name="hashIterations" value="1024"></property>
    		</bean>
    	</property>
    </bean>

    <!-- 
    4、配置lifecycleBeanPostProcessor,可以自动的来调用配置在Spring IOC 容器中的shiro bean的生命周期方法
     -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- 
    5、启用IOC容器中使用shiro的注解。但必须在配置了lifecycleBeanPostProcessor之后才可以使用
     -->
    <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>

    <!-- 
    6、配置shiroFilter
    	6.1、id必须和web.xml中的配置的DelegatingFilterProxy的<filter-name>的值一致
     		若不一致会抛出异常NoSuchBeanDefinitionException。因为shiro会来IOC容器中查找和<filter-name>名字对应的filter bean
     -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.jsp"/>
        <property name="successUrl" value="/list.jsp"/>
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
        
        <!-- 可以从数据表中初始化资源和权限,代替下面使用 filterChainDefinitions写死的权限配置-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
        <!-- 
        	URL匹配模式:
        	?:匹配一个字符。如/admin将匹配/admin1,但不匹配/admin或/admin/
        	*:匹配0个或多个字符串。如/admin*将匹配/admin、/admin123,但不匹配/admin/1
        	**:匹配路径中的零个或多个路径。如/admin/**将匹配/admin/a或admin/a/b
        
        	配置哪些页面需要受保护,以及访问这些页面需要的权限,这里的URL权限采取第一次匹配优先的方式
        	1、anon 可以被匿名访问
        	2、authc 必须认证(即登录)后才可以访问的页面
        	3、logout 注销
        	4、roles 角色过滤器
         -->
         <!-- 
        <property name="filterChainDefinitions">
            <value>
                /login.jsp = anon
                /shiroCtrl/login = anon
                /shiroCtrl/logout = logout
                
                /user.jsp = roles[user]
                /admin.jsp = roles[admin]
                # everything else requires authentication:
                /** = authc
            </value>
        </property>
         -->
    </bean>
	
	<!--配置一个bean,该bean实际上是一个map,通过实例工厂方法的方式  -->
	<bean id="filterChainDefinitionMap"
		factory-bean="filterChainDefinitionMapBuilder"
		factory-method="buildFilterChainDefinitionMapBuilder"></bean>
	<bean id="filterChainDefinitionMapBuilder"
		class="com.mfc.factory.FilterChainDefinitionMapBuilder"></bean>
		
    <!-- 注入测试注解配置权限的service  -->
	<bean id="shiroService" class="com.mfc.service.ShiroService"></bean>
</beans>

4、创建Realm并要在Spring IOC容器中配置,这里会用了两个Realm,当然可以使用更多个。Realm认证策略参照:Shiro——认证

package com.mfc.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.LockedAccountException;
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.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**
 * @author 74790
 * 1.授权需要继承AuthorizingRealm,并实现doGetAuthorizationInfo 方法
   2.AuthorizingRealm类继承自AuthenticatingRealm,但没有实现AuthenticatingRealm中的
	 doGetAuthenticationInfo,所以认证和授权只需要继承AuthorizingRealm就可以了,同时实现他的
	   两个抽象方法。
 */
public class ShiroRealm extends AuthorizingRealm {
	
	// 认证的方法
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("[ShiroRealm] doGetAuthenticationInfo");

		//1、把AuthenticationToken转化为UsernamePasswordToken
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		
		//2、UsernamePasswordToken中获取Username
		String username = upToken.getUsername();
		
		//3、调用数据库方法,从数据库中查询username对用的用户记录
		System.out.println("从数据库中获取username:" + username + " 所对应的用户信息");
		
		//4、若用户不存在,可以抛出UnknownAccountException异常
		if("unknown".equals(username)){
			throw new UnknownAccountException("用户不存在");
		}
		
		//5、根据用户信息的情况,决定是否需要抛出其他AuthenticationException异常
		if("monster".equals(username)){
			throw new LockedAccountException("用户被锁定");
		}
		
		//6、根据用户的情况来构建AuthenticationInfo对象并返回,通常使用的实现类是SimpleAuthenticationInfo
		//以下信息是从数据库中获取的
		//principal:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象
		Object principal = username;
		//credentials:密码
		Object credentials = null;
		if("admin".equals(username)){
			credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
		}else if("user".equals(username)){
			credentials = "098d2c478e9c11555ce2823231e02ec1";
		}
		//realmName:当前realm 对象的name,调用父类的getName()方法即可
		String realmName = getName();
		//盐值,防止两用户密码一样的时候不安全
		ByteSource credentialsSalt = ByteSource.Util.bytes(username);
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
		return info;
	}
	
	// 授权需要实现的方法
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//1.从PrincipalCollection中获取登陆用户的信息
		Object principal = principals.getPrimaryPrincipal();
		
		//2.利用登陆用户的信息来获取当前用户的角色和权限(可能需要查询数据库)
		Set<String> roles = new HashSet<String>();
		roles.add("user");
		if("admin".equals(principal)){
			roles.add("admin");
		}
		
		//3.创建SimpleAuthorizationInfo对象,并设置其roles
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
		
		//4.返回SimpleAuthorizationInfo对象
		return info;
	}
	
	public static void main(String[] args) {
		String hashIterations = "MD5";
		Object credentials = "123456";
		Object salt = ByteSource.Util.bytes("user");;
		int algorithmName = 1024;
		
		Object result = new SimpleHash(hashIterations, credentials, salt, algorithmName);
		System.out.println("result:" + result);
	}
	

}
package com.mfc.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;

/**
 * @author 74790
 * 认证的Realm
 */
public class SecondRealm extends AuthenticatingRealm {

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("[SecondRealm] doGetAuthenticationInfo");
		//1、把AuthenticationToken转化为UsernamePasswordToken
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		
		//2、UsernamePasswordToken中获取Username
		String username = upToken.getUsername();
		
		//3、调用数据库方法,从数据库中查询username对用的用户记录
		System.out.println("从数据库中获取username:" + username + " 所对应的用户信息");
		
		//4、若用户不存在,可以抛出UnknownAccountException异常
		if("unknown".equals(username)){
			throw new UnknownAccountException("用户不存在");
		}
		
		//5、根据用户信息的情况,决定是否需要抛出其他AuthenticationException异常
		if("monster".equals(username)){
			throw new LockedAccountException("用户被锁定");
		}
		
		//6、根据用户的情况来构建AuthenticationInfo对象并返回,通常使用的实现类是SimpleAuthenticationInfo
		//以下信息是从数据库中获取的
		//principal:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象
		Object principal = username;
		//credentials:密码
		//Object credentials = "fc1709d0a95a6be30bc5926fdb7f22f4";
		Object credentials = null;
		if("admin".equals(username)){
			credentials = "ce2f6417c7e1d32c1d81a797ee0b499f87c5de06";
		}else if("user".equals(username)){
			credentials = "073d4c3ae812935f23cb3f2a71943f49e082a718";
		}
		//realmName:当前realm 对象的name,调用父类的getName()方法即可
		String realmName = getName();
		//SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
		//盐值
		ByteSource credentialsSalt = ByteSource.Util.bytes(username);
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
		return info;
	}
	
	public static void main(String[] args) {
		String hashIterations = "SHA1";
		Object credentials = "123456";
		Object salt = ByteSource.Util.bytes("admin");;
		int algorithmName = 1024;
		
		Object result = new SimpleHash(hashIterations, credentials, salt, algorithmName);
		System.out.println("result:" + result);
	}
	

}

5、测试注解配置权限的service:关于注解的使用可以看上面的四

package com.mfc.service;

import java.util.Date;

import org.apache.shiro.authz.annotation.RequiresRoles;

/**
 * @author 74790
 * Shiro的注解方式配置在service或者是Controller中
 */
public class ShiroService {
	/*
	 * 只有admin角色才可以访问这个方法
	 * */
	@RequiresRoles({"admin"})
	public void test(){
		System.out.println("ShiroService测试权限:" + new Date());
	}
}

6、Controller:

package com.mfc.ctrl;

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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.mfc.service.ShiroService;

@Controller
@RequestMapping(value = "shiroCtrl")
public class ShiroCtrl {
	
	@Autowired
	private ShiroService shiroService;
	
	@RequestMapping(value = "test")
	public String test(){
		shiroService.test();
		return "redirect:/list.jsp";
	}
	
	@RequestMapping(value = "login")
	public String login(@RequestParam("username")String username, 
			@RequestParam("password")String password, Model model){
		Subject currentUser = SecurityUtils.getSubject();
		
        // 测试当前的用户是否已经被认证,即是否已经登录
        // 调用Subject 的isAuthenticated()
        if (!currentUser.isAuthenticated()) {
        	// 把用户名和密码封装为UsernamePasswordToken 对象
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            // RememberMe
            token.setRememberMe(true);
            try {
            	// 执行登录
            	// 这里的token其实传到了ShiroRealm.doGetAuthenticationInfo(AuthenticationToken token)
                currentUser.login(token);
            }catch (AuthenticationException ae) {
            	// 所有认证时异常的父类
            	System.err.println("登录失败:"+ae.getMessage());
            }
        }
		return "redirect:/list.jsp";
	}
}

7、从数据库中获取权限配置资源的bean:这个bean需要在Spring IOC容器中注入,可以查看上面的applicationContext.xml配置文件。

package com.mfc.factory;

import java.util.LinkedHashMap;

/**
 * @author 74790
 * 使用bean配置的方式,将权限配置配置到数据库里面去
 */
public class FilterChainDefinitionMapBuilder {

	public LinkedHashMap<String, String> buildFilterChainDefinitionMapBuilder(){
		LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
		
		/*
		 *
		这些可以存到数据库里面去,在这里取出来即可,注意放进map的顺序
		/login.jsp = anon
	    /shiroCtrl/login = anon
	    /shiroCtrl/logout = logout
	    
	    /user.jsp = roles[user]
	    /admin.jsp = roles[admin]
	    # everything else requires authentication:
	    /** = authc
		 * */
		map.put("/login.jsp", "anon");
		map.put("/shiroCtrl/login", "anon");
		map.put("/shiroCtrl/logout", "logout");
		map.put("/user.jsp", "roles[user]");
		map.put("/admin.jsp", "roles[admin]");
		map.put("/**", "authc");
		return map;
	
	}
}

8、JSP代码这里省略。

猜你喜欢

转载自blog.csdn.net/fancheng614/article/details/83477345