Shiro 整合 spring hello world

shiro 是一个Java安全框架可以进行认证、授权、加密和会话管理。Shiro的易于理解的API适用于保护任何应用程序——从最小的移动应用程序到最大的Web和企业应用程序。

【官方解释:

Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro's easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.】

接下来是一个应用shiro 的使用步骤,由于习惯直接写注释到代码中,因此步骤文字就简写啦

一、导入所需jar包

        

二、配置web.xml, 包括配置spring、spring-mvc以及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_2_5.xsd" 
	id="WebApp_ID" version="2.5">

<!-- spring 环境配置:start -->
    <!-- 配置加载 Spring 文件的监听器,加载 applicationContext.xml 文件 -->  
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>
	<!-- Spring监听器 --> 
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
<!-- spring 环境配置:end -->


<!-- 配置 spring-mvc:start -->
	<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<!-- spring 默认会去 WEB-INF 下找名字为 <servlet-name>-servlet.xml的配置文件, 或可以设置<init-param> 指定配置文件位置 -->
	<servlet>
		<servlet-name>spring</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
<!-- 配置 spring-mvc:end -->



<!-- 1. 配置 shiro 的 shiroFilter -->
	<!-- ==================================================================
         Filters
         ================================================================== -->
    <!-- Shiro Filter is defined in the spring application context: -->
    <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>
<!--  配置 shiro 的 shiroFilter: end -->

	<welcome-file-list>
		<welcome-file>login.jsp</welcome-file>
	</welcome-file-list>
</web-app>

三、配置spring-mvc的配置文件(spring-servlet.xml),由于web.xml中使用的是默认方式,该文件需要放于WEB-INF目录下

【不是用默认位置的话可以通过在spring-mvc配置的servlet中增加如下标签来指定: 

      <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>classpath:spring-mvc.xml</param-value>  
        </init-param>  】

<?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:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.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.2.xsd">

	<!-- 扫描包 -->
	<context:component-scan base-package="com.shiro"></context:component-scan>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
		<!-- 这里的配置我的理解是自动给后面action的方法return的字符串加上前缀和后缀,变成一个 可用的url地址  -->
		<property name="prefix" value="/" />  
        <property name="suffix" value=".jsp" />  
    </bean>  
    
    <mvc:annotation-driven></mvc:annotation-driven>
    <mvc:default-servlet-handler/>

</beans>

四、配置spring主配置文件applicationContext.xml, shiro 的配置也整合于此:

(note: 此处配置了两个realm, 即两重验证,不需要的可直接干掉一个)

<?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">


<!-- 1. 配置 SecurityManager. 核心 -->
    <!-- =========================================================
         Shiro Core Components - Not Spring Specific
         ========================================================= -->
    <!-- Shiro's main business-tier object for web-enabled applications
         (use DefaultSecurityManager instead when there is no web environment)-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="authenticator" ref="authenticator"></property>
        <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
        <!-- 配置 session 的管理方式  <property name="sessionMode" value="native"/>  -->
        <property name="realms">
        	<list>
        		<ref bean="jdbcRealm" />
        		<ref bean="secondRealm" />
        	</list>
        </property> 
        <!-- <property name="realm" ref="jdbcRealm"></property> -->
        
    </bean>
<!-- 1. 配置 SecurityManager : end -->

<!-- 2. 配置 cacheManager.   -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!-- Set a net.sf.ehcache.CacheManager instance here if you already have one.  If not, a new one
             will be creaed with a default config:
             <property name="cacheManager" ref="ehCacheManager"/> -->
        <!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
             a specific Ehcache configuration to be used, specify that here.  If you don't, a default
             will be used.:
             -->
        <!-- 2.1 加入encache 的 jar包及 cache 的配置文件 -->
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> 
    </bean>
<!-- 2. 配置 cacheManager : end  -->


<!--  配置多 realm 及验证策略  -->
	<bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
		<!--
		<property name="realms">
			 list 验证的时候有顺序 
			<list>
				<ref bean="jdbcRealm"/>
				另一个realm 的bean  
				<ref bean="secondRealm" />
			</list>
		</property> 
		-->
		<!-- 配置认证策略  -->
		<property name="authenticationStrategy">
			<!-- AllSuccessfulStrategy 全部验证成功才成功;或(默认)FirstSuccessfulStrategy/AtLeastOneSuccessfulStrategy -->
			<bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy" ></bean>
		</property>
	</bean>
<!--  配置多 realm 及验证策略  :end -->

<!-- 3. 配置 Realm
		3.1 直接配置实现接口 Realm 的bean
 -->
    <!-- Used by the SecurityManager to access security data (users, roles, etc).
         Many other realm implementations can be used too (PropertiesRealm,
         LdapRealm, etc. -->
    <bean id="jdbcRealm" class="com.shiro.Realms.ShiroRealm">
        <property name="credentialsMatcher">
        	<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" >
        		<!--  加密的算法  -->
        		<property name="hashAlgorithmName" value="MD5"></property>
        		<!-- hashIterations 加密的次数  -->
        		<property name="hashIterations" value="1024"></property>
        	</bean>
        </property> 
    </bean>
    
    <!-- 第二个 Realm -->
    <bean id="secondRealm" class="com.shiro.Realms.SecondRealm">
        <property name="credentialsMatcher">
        	<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" >
        		 <!-- 加密的算法   -->
        		<property name="hashAlgorithmName" value="SHA1"></property>
        		 <!-- hashIterations 加密的次数  -->
        		<property name="hashIterations" value="1024"></property>
        	</bean>
        </property>
    </bean>
<!-- 3. 配置 Realm :end -->


<!-- 4. 配置 LifecycleBeanPostProcessor, 可以自动的来调用配置在 spring IOC 容器中的shiro bean 的声明周期的方法 -->
	<!-- =========================================================
         Shiro Spring-specific integration
         ========================================================= -->
    <!-- Post processor that automatically invokes init() and destroy() methods
         for Spring-configured Shiro objects so you don't have to
         1) specify an init-method and destroy-method attributes for every bean
            definition and
         2) even know which Shiro objects require these methods to be
            called. -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 4. 配置 LifecycleBeanPostProcessor : end -->

<!-- 5. 启用IOC 容器中使用 shiro 的注解,但必须在配置了 LifecycleBeanPostProcessor 之后使用 -->
	<!-- Enable Shiro Annotations for Spring-configured beans.  Only run after
         the lifecycleBeanProcessor has run: -->
    <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>
<!-- 5. 启用IOC 容器中使用 shiro 的注解 : end -->

<!-- 6. 配置ShiroFilter(重点) -->
<!-- 	6.1 id 必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致. -->
	<!-- Define the Shiro Filter here (as a FactoryBean) instead of directly in web.xml -
         web.xml uses the DelegatingFilterProxy to access this bean.  This allows us
         to wire things with more control as well utilize nice Spring things such as
         PropertiesPlaceholderConfigurer and abstract beans or anything else we might need: -->
    <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"/>
        
        <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean
             defined will be automatically acquired and available via its beanName in chain
             definitions, but you can perform overrides or parent/child consolidated configuration
             here if you like: -->
        <!-- <property name="filters">
            <util:map>
                <entry key="aName" value-ref="someFilterPojo"/>
            </util:map>
        </property> -->
        <!-- 
        	配置哪些页面需要受保护
        	以及访问这些页面需要的权限
        	1) anon 可以被匿名访问
        	2) authc 必须认证(登录)后才能够访问
        	3) logout 登出
        	第一次匹配优先
         -->
        <property name="filterChainDefinitions">
            <value>
                /login.jsp = anon
                /shiro/login = anon
                /shiro/logout = logout
                
                # 角色权限配置
                /user.jsp = roles[user]
                /admin.jsp = roles[admin]
               
                # everything else requires authentication:
                /** = authc
            </value>
        </property>
    </bean>
<!-- 6. 配置ShiroFilter(重点) : end -->

</beans>

五、导入必要的配置文件 ehcache.xml 和 log4j.properties(ehcache.xml 可在hibernate 的包中找到,log4j.properties 也比较常见就不贴了)

六、写Realm类,继承于AuthorizingRealm

package com.shiro.Realms;

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;


public class ShiroRealm extends AuthorizingRealm {

	/**
	 * 认证
	 * 
	 * 使用 盐值加密
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
			throws AuthenticationException {
		System.out.println("===> FirstRealm: ");
		System.out.println("---> doGetAuthenticationInfo: " + token.hashCode());
		
		// 1. 把 AuthenticationToken 转换为 UsernamePasswordToken
		UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
		
		// 2. 从 UsernamePasswordToken 中获取 username
		String username = usernamePasswordToken.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 对象,并返回
		//
		// 以下信息是从数据库中获取的
		// 1) principal: 认证的实体信息,可以是 username 也可以是数据库对应用户的实体对象
		// 2) credentials: 密码(来自数据库)
		// 3) realmName: 当前 realm 对象的name, 调用父类的 getName() 方法即可.
		 
		Object principal = username;
		Object credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";	// 加密后的密码; 原始密码为 123456,盐为 "admin"
		String realmName = getName();

		// 用户名作为盐值
		ByteSource credentialsSalt = ByteSource.Util.bytes(username);
		
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
		
		return info;
	}

	public static void main(String[] args) {
		String algorithmName = "MD5";
		Object source = "123456";	// 原始密码
		Object salt = "admin";		// 盐值
		int hashIterations = 1024;	// 加密的次数
		
		Object result = new SimpleHash(algorithmName, source, salt, hashIterations);// hashIterations 加密的次数
		
		System.out.println("盐值加密后的密码:" + result);
	}

	/**
	 * 授权
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		// 1. 从 PrincipalCollection 中获取登陆用户的信息
		Object principal = principals.getPrimaryPrincipal();
		
		// 2. 利用登陆的用户信息来授权给当前用户的角色或者权限(可能需要查询数据库)
		Set<String> roles = new HashSet<>();
		roles.add("user");
		if ("admin".equals(principal)) {
			roles.add("admin");
		}
		
		// 3. 创建 SimpleAuthorizationInfo, 并设置其 role 属性
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
		
		// 4. 返回 对象
		return info;
	}
}

第二个Realm类与 ShiroRealm只是加密算法改为sha1而已。不过需要注意的是由于没有使用数据库,因此密码都是加密后写死的,使用时需据实修改。

六、处理web请求的handler类

package com.shiro.handlers;

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 org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/shiro")
public class ShiroHandler {

	@RequestMapping("/login")
	public String login(@RequestParam("username") String username, 
			@RequestParam("password") String password) {
		Subject currentUser = SecurityUtils.getSubject();
		
		if(!currentUser.isAuthenticated()) {
			UsernamePasswordToken token = new UsernamePasswordToken(username, password);
			// 记住我
			token.setRememberMe(true);
			try {
				System.out.println("---> 1. " + token.hashCode());
				// 执行登录
				currentUser.login(token);
			} catch (AuthenticationException ae) {
				System.out.println("登录失败: " + ae.getMessage());
			}
		}
		
		return "redirect:/list.jsp";
	}
	
}

七、最后添加个登陆和几个可区分情况跳转的页面,至此hello world也就写好了



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值