Shiro单Realm加密

首先,我们要明确认证的流程:

  1. 获取当前的 Subject. 调用 SecurityUtils.getSubject();
  2. 测试当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()
  3. 若没有被认证, 则把用户名和密码封装为 UsernamePasswordToken 对象
    1). 创建一个表单页面
    2). 把请求提交到 SpringMVC 的 Handler
    3). 获取用户名和密码.
  4. 执行登录: 调用 Subject 的 login(AuthenticationToken) 方法.
  5. 自定义 Realm 的方法, 从数据库中获取对应的记录, 返回给 Shiro.
    1). 实际上需要继承 org.apache.shiro.realm.AuthenticatingRealm 类
    2). 实现 doGetAuthenticationInfo(AuthenticationToken) 方法.
  6. 由 shiro 完成对密码的比对..

密码的比对:
  通过 AuthenticatingRealm 的 credentialsMatcher 属性来进行的密码的比对!

如何把一个字符串加密为 MD5
  替换当前 Realm 的 credentialsMatcher 属性. 直接使用 HashedCredentialsMatcher 对象, 并设置加密算法即可.

为什么使用 MD5 盐值加密?如何做到?
  如何做到:
  1). 在 doGetAuthenticationInfo 方法返回值创建 SimpleAuthenticationInfo 对象的时候, 需要使用
  SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName) 构造器
  2). 使用 ByteSource.Util.bytes() 来计算盐值.
  3). 盐值需要唯一: 一般使用随机字符串或 user id
  4). 使用 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); 来计算盐值加密后的密码的值.

接下来,我们进行代码的编写和配置文件的相关配置

applicationContext.xml

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <beans xmlns="http://www.springframework.org/schema/beans"
  3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5 
  6     <!-- =========================================================
  7          Shiro Core Components - Not Spring Specific
  8          ========================================================= -->
  9     <!-- Shiro's main business-tier object for web-enabled applications
 10          (use DefaultSecurityManager instead when there is no web environment)-->
 11     <!-- 
 12         1.配置SecurityManager!
 13      -->
 14     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
 15         <property name="cacheManager" ref="cacheManager"/>
 16         <property name="realm" ref="jdbcRealm"/>
 17     </bean>
 18 
 19     <!-- Let's use some enterprise caching support for better performance.  You can replace this with any enterprise
 20          caching framework implementation that you like (Terracotta+Ehcache, Coherence, GigaSpaces, etc -->
 21     
 22     <!-- 
 23     2.配置cacheManager
 24     2.1 需要加入ehcache的jar包及配置文件    
 25      -->
 26     <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
 27         <!-- Set a net.sf.ehcache.CacheManager instance here if you already have one.  If not, a new one
 28              will be creaed with a default config:
 29              <property name="cacheManager" ref="ehCacheManager"/> -->
 30         <!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
 31              a specific Ehcache configuration to be used, specify that here.  If you don't, a default
 32              will be used.: -->
 33         <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
 34     </bean>
 35 
 36     <!-- Used by the SecurityManager to access security data (users, roles, etc).
 37          Many other realm implementations can be used too (PropertiesRealm,
 38          LdapRealm, etc. -->
 39     
 40     <!-- 
 41     3.配置Realm
 42     3.1直接配置实现了com.atguigu.shiro.realms.ShiroRealm接口的bean
 43      -->
 44     <bean id="jdbcRealm" class="com.atguigu.shiro.realms.ShiroRealm">
 45         <!-- 配置MD5加密 -->
 46         <property name="credentialsMatcher">
 47             <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
 48                 <!-- 指定加密方式为MD5 -->
 49                 <property name="hashAlgorithmName" value="MD5"></property>
 50                 <!-- 指定加密次数 -->
 51                 <property name="hashIterations" value="1024"></property>
 52             </bean>
 53         </property>
 54     </bean>
 55 
 56     <!-- =========================================================
 57          Shiro Spring-specific integration
 58          ========================================================= -->
 59     <!-- Post processor that automatically invokes init() and destroy() methods
 60          for Spring-configured Shiro objects so you don't have to
 61          1) specify an init-method and destroy-method attributes for every bean
 62             definition and
 63          2) even know which Shiro objects require these methods to be
 64             called. -->
 65     <!-- 
 66     4.配置LifecycleBeanPostProcessor,可以自动地来调用配置在Spring IOC容器中shiro bean的生命周期方法
 67      -->
 68     <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
 69 
 70     <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after
 71          the lifecycleBeanProcessor has run: -->
 72     
 73     <!-- 
 74     5.启用IOC容器中使用shiro的注解,但必须在配置了LifecycleBeanPostProcessor之后才可以使用
 75      -->
 76     <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
 77           depends-on="lifecycleBeanPostProcessor"/>
 78     <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
 79         <property name="securityManager" ref="securityManager"/>
 80     </bean>
 81 
 82     <!-- Define the Shiro Filter here (as a FactoryBean) instead of directly in web.xml -
 83          web.xml uses the DelegatingFilterProxy to access this bean.  This allows us
 84          to wire things with more control as well utilize nice Spring things such as
 85          PropertiesPlaceholderConfigurer and abstract beans or anything else we might need: -->
 86     
 87     <!-- 
 88     6.配置ShiroFilter
 89     6.1 id必须和web.xml文件中配置的DelegatingFilterProxy的 <filter-name> 一致
 90         若不一致,则会抛出:NoSuchBeanDefinitionException. 因为 Shiro 会来 IOC 容器中查找和 <filter-name> 名字对应的 filter bean.
 91     6.2
 92      -->
 93     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
 94         <property name="securityManager" ref="securityManager"/>
 95         <property name="loginUrl" value="/login.jsp"/>
 96         <property name="successUrl" value="/list.jsp"/>
 97         <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
 98         
 99         <!-- 
100             配置哪些页面需要受保护
101             以及访问这些页面需要的权限
102             1). anon 可以被匿名访问
103             2). authc 必须认证(即登录)后才可以访问的页面
104             3). logout 登出
105          -->
106         <property name="filterChainDefinitions">
107             <value>
108                 /login.jsp = anon
109                 /shiro/login = anon
110                 /shiro/logout = logout
111                 # everything else requires authentication:
112                 /** = authc
113             </value>
114         </property>
115     </bean>
116 </beans>

ShiroHandler.java

 1 package com.atguigu.shiro.handlers;
 2 
 3 import org.apache.shiro.SecurityUtils;
 4 import org.apache.shiro.authc.AuthenticationException;
 5 import org.apache.shiro.authc.UsernamePasswordToken;
 6 import org.apache.shiro.subject.Subject;
 7 import org.springframework.stereotype.Controller;
 8 import org.springframework.web.bind.annotation.RequestMapping;
 9 import org.springframework.web.bind.annotation.RequestParam;
10 
11 @Controller
12 @RequestMapping("/shiro")
13 public class ShiroHandler {
14 
15     @RequestMapping("/login")
16     public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
17         // 获取当前的 Subject. 调用 SecurityUtils.getSubject();
18         Subject currentUser  = SecurityUtils.getSubject();
19         // 测试当前的用户是否已经被认证. 即是否已经登录.
20         // 调动 Subject 的 isAuthenticated()
21         if (!currentUser.isAuthenticated()) {
22             // 把用户名和密码封装为 UsernamePasswordToken 对象
23             UsernamePasswordToken token = new UsernamePasswordToken(username,password);
24             // rememberme
25             token.setRememberMe(true);
26             try {
27                 System.out.println("1." + token.hashCode());
28                 // 执行登录.
29                 currentUser.login(token);
30             }
31             // 所有认证时异常的父类.
32             catch (AuthenticationException ae) {
33                 System.out.println("登录失败:"+ae.getMessage());
34             }
35         }
36         return "redirect:/list.jsp";
37     }
38 }

ShiroRealm.java

 1 package com.atguigu.shiro.realms;
 2 
 3 import org.apache.shiro.authc.AuthenticationException;
 4 import org.apache.shiro.authc.AuthenticationInfo;
 5 import org.apache.shiro.authc.AuthenticationToken;
 6 import org.apache.shiro.authc.LockedAccountException;
 7 import org.apache.shiro.authc.SimpleAuthenticationInfo;
 8 import org.apache.shiro.authc.UnknownAccountException;
 9 import org.apache.shiro.authc.UsernamePasswordToken;
10 import org.apache.shiro.crypto.hash.SimpleHash;
11 import org.apache.shiro.realm.AuthenticatingRealm;
12 import org.apache.shiro.util.ByteSource;
13 
14 public class ShiroRealm extends AuthenticatingRealm {
15 
16     @Override
17     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
18 
19         System.out.println("doGetAuthenticationInfo " + token.hashCode());
20         
21         //1. 把AuthenticationToken 转换为 UsernamePasswordToken
22         UsernamePasswordToken upToken = (UsernamePasswordToken)token;
23         
24         //2. 从UsernamePasswordToken 中来获取username
25         String username = upToken.getUsername();
26         
27         //3.调用数据库的方法,从数据库查询username 对应的用户记录
28         System.out.println("从数据库中获取username:" + username +" 所对应的用户信息");
29         
30         //4.若用户不存在可以抛出 UnknownAccountException 异常
31         if ("unknown".equals(username)) {
32             throw new UnknownAccountException("用户不存在");
33         }
34         
35         //5.根据用户信息的情况,决定是否抛出其它的 AuthenticationException 异常
36         if ("monster".equals(username)) {
37             throw new LockedAccountException("用户被锁定");
38         }
39         
40         //6.根据用户的情况,来构建 AuthenticationToken 对象并返回,通常使用的实现类为:SimpleAuthenticationInfo 
41         //一下信息是从数据库中获取的
42         //1).principals:认证的实体信息,可以是username,也可以是数据表对应的实体类对象
43         Object principal = username;
44         //2).credentials:密码
45         Object hashedCredentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";
46         if ("admin".equals(username)) {
47             hashedCredentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
48         }else if("user".equals(username)) {
49             hashedCredentials = "098d2c478e9c11555ce2823231e02ec1";
50         }
51         
52         
53         //3).realmName:当前realm 对象的name,调用父类的getName()方法即可
54         String realmName = getName();
55         //4).盐值
56         //SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credential,realmName);
57         //因为用户名唯一,使用用户名作为盐值
58         ByteSource credentialsSalt = ByteSource.Util.bytes(username);
59         SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName);
60         return info;
61     }
62     
63     public static void main(String[] args) {
64         String algorithmName = "MD5";
65         Object password = "123456";
66         Object salt = ByteSource.Util.bytes("user");
67         int hashIterations = 1024;
68         Object result = new SimpleHash(algorithmName, password, salt, hashIterations);
69         System.out.println(result);
70     }
71 
72 }

ehcache.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <ehcache updateCheck="false" dynamicConfig="false">
 3     <diskStore path="java.io.tmpdir"/>
 4     <!--授权信息缓存-->
 5     <cache name="authorizationCache"
 6            maxEntriesLocalHeap="2000"
 7            eternal="false"
 8            timeToIdleSeconds="1800"
 9            timeToLiveSeconds="1800"
10            overflowToDisk="false"
11            statistics="true">
12     </cache>
13 <!--身份信息缓存-->
14     <cache name="authenticationCache"
15            maxEntriesLocalHeap="2000"
16            eternal="false"
17            timeToIdleSeconds="1800"
18            timeToLiveSeconds="1800"
19            overflowToDisk="false"
20            statistics="true">
21     </cache>
22 <!--session缓存-->
23     <cache name="activeSessionCache"
24            maxEntriesLocalHeap="2000"
25            eternal="false"
26            timeToIdleSeconds="1800"
27            timeToLiveSeconds="1800"
28            overflowToDisk="false"
29            statistics="true">
30     </cache>
31 
32     <!-- 缓存半小时 -->
33     <cache name="halfHour"
34            maxElementsInMemory="10000"
35            maxElementsOnDisk="100000"
36            eternal="false"
37            timeToIdleSeconds="1800"
38            timeToLiveSeconds="1800"
39            overflowToDisk="false"
40            diskPersistent="false" />
41 
42     <!-- 缓存一小时 -->
43     <cache name="hour"
44            maxElementsInMemory="10000"
45            maxElementsOnDisk="100000"
46            eternal="false"
47            timeToIdleSeconds="3600"
48            timeToLiveSeconds="3600"
49            overflowToDisk="false"
50            diskPersistent="false" />
51 
52     <!-- 缓存一天 -->
53     <cache name="oneDay"
54            maxElementsInMemory="10000"
55            maxElementsOnDisk="100000"
56            eternal="false"
57            timeToIdleSeconds="86400"
58            timeToLiveSeconds="86400"
59            overflowToDisk="false"
60            diskPersistent="false" />
61 
62     <!--
63         name:缓存名称。
64         maxElementsInMemory:缓存最大个数。
65         eternal:对象是否永久有效,一但设置了,timeout将不起作用。
66         timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
67         timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
68         overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
69         diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
70         maxElementsOnDisk:硬盘最大缓存个数。
71         diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
72         diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
73         memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
74         clearOnFlush:内存数量最大时是否清除。
75     -->
76     <defaultCache name="defaultCache"
77                   maxElementsInMemory="10000"
78                   eternal="false"
79                   timeToIdleSeconds="600"
80                   timeToLiveSeconds="600"
81                   overflowToDisk="false"
82                   maxElementsOnDisk="100000"
83                   diskPersistent="false"
84                   diskExpiryThreadIntervalSeconds="120"
85                   memoryStoreEvictionPolicy="LRU"/>
86 
87 </ehcache>

login.jsp

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html>
 4 <html>
 5 <head>
 6 <meta charset="UTF-8">
 7 <title>Insert title here</title>
 8 </head>
 9 <body>
10     <h4>Login Page</h4>
11     <form action="shiro/login" method="post">
12         username:<input type="text" name="username"/>
13         <br/><br/>
14         password:<input type="password" name="password"/>
15         <br/><br/>
16         <input type="submit" value="Submit"/>
17     </form>
18 </body>
19 </html>
原文地址:https://www.cnblogs.com/116970u/p/11171319.html