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

  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

 <?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"> <!-- =========================================================
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)-->
<!--
1.配置SecurityManager!
-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager"/>
<property name="realm" ref="jdbcRealm"/>
</bean> <!-- Let's use some enterprise caching support for better performance. You can replace this with any enterprise
caching framework implementation that you like (Terracotta+Ehcache, Coherence, GigaSpaces, etc --> <!--
2.配置cacheManager
2.1 需要加入ehcache的jar包及配置文件
-->
<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.: -->
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean> <!-- Used by the SecurityManager to access security data (users, roles, etc).
Many other realm implementations can be used too (PropertiesRealm,
LdapRealm, etc. --> <!--
3.配置Realm
3.1直接配置实现了com.atguigu.shiro.realms.ShiroRealm接口的bean
-->
<bean id="jdbcRealm" class="com.atguigu.shiro.realms.ShiroRealm">
<!-- 配置MD5加密 -->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 指定加密方式为MD5 -->
<property name="hashAlgorithmName" value="MD5"></property>
<!-- 指定加密次数 -->
<property name="hashIterations" value="1024"></property>
</bean>
</property>
</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. -->
<!--
4.配置LifecycleBeanPostProcessor,可以自动地来调用配置在Spring IOC容器中shiro bean的生命周期方法
-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- Enable Shiro Annotations for Spring-configured beans. Only run after
the lifecycleBeanProcessor has run: --> <!--
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> <!-- 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: --> <!--
6.配置ShiroFilter
6.1 id必须和web.xml文件中配置的DelegatingFilterProxy的 <filter-name> 一致
若不一致,则会抛出:NoSuchBeanDefinitionException. 因为 Shiro 会来 IOC 容器中查找和 <filter-name> 名字对应的 filter bean.
6.2
-->
<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"/> <!--
配置哪些页面需要受保护
以及访问这些页面需要的权限
1). anon 可以被匿名访问
2). authc 必须认证(即登录)后才可以访问的页面
3). logout 登出
-->
<property name="filterChainDefinitions">
<value>
/login.jsp = anon
/shiro/login = anon
/shiro/logout = logout
# everything else requires authentication:
/** = authc
</value>
</property>
</bean>
</beans>

ShiroHandler.java

 package com.atguigu.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. 调用 SecurityUtils.getSubject();
Subject currentUser = SecurityUtils.getSubject();
// 测试当前的用户是否已经被认证. 即是否已经登录.
// 调动 Subject 的 isAuthenticated()
if (!currentUser.isAuthenticated()) {
// 把用户名和密码封装为 UsernamePasswordToken 对象
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
// rememberme
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";
}
}

ShiroRealm.java

 package com.atguigu.shiro.realms;

 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; public class ShiroRealm extends AuthenticatingRealm { @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("doGetAuthenticationInfo " + token.hashCode()); //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.根据用户的情况,来构建 AuthenticationToken 对象并返回,通常使用的实现类为:SimpleAuthenticationInfo
//一下信息是从数据库中获取的
//1).principals:认证的实体信息,可以是username,也可以是数据表对应的实体类对象
Object principal = username;
//2).credentials:密码
Object hashedCredentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";
if ("admin".equals(username)) {
hashedCredentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
}else if("user".equals(username)) {
hashedCredentials = "098d2c478e9c11555ce2823231e02ec1";
} //3).realmName:当前realm 对象的name,调用父类的getName()方法即可
String realmName = getName();
//4).盐值
//SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credential,realmName);
//因为用户名唯一,使用用户名作为盐值
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName);
return info;
} public static void main(String[] args) {
String algorithmName = "MD5";
Object password = "123456";
Object salt = ByteSource.Util.bytes("user");
int hashIterations = 1024;
Object result = new SimpleHash(algorithmName, password, salt, hashIterations);
System.out.println(result);
} }

ehcache.xml

 <?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
<diskStore path="java.io.tmpdir"/>
<!--授权信息缓存-->
<cache name="authorizationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
overflowToDisk="false"
statistics="true">
</cache>
<!--身份信息缓存-->
<cache name="authenticationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
overflowToDisk="false"
statistics="true">
</cache>
<!--session缓存-->
<cache name="activeSessionCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
overflowToDisk="false"
statistics="true">
</cache> <!-- 缓存半小时 -->
<cache name="halfHour"
maxElementsInMemory="10000"
maxElementsOnDisk="100000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
overflowToDisk="false"
diskPersistent="false" /> <!-- 缓存一小时 -->
<cache name="hour"
maxElementsInMemory="10000"
maxElementsOnDisk="100000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="false"
diskPersistent="false" /> <!-- 缓存一天 -->
<cache name="oneDay"
maxElementsInMemory="10000"
maxElementsOnDisk="100000"
eternal="false"
timeToIdleSeconds="86400"
timeToLiveSeconds="86400"
overflowToDisk="false"
diskPersistent="false" /> <!--
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
-->
<defaultCache name="defaultCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
overflowToDisk="false"
maxElementsOnDisk="100000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/> </ehcache>

login.jsp

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h4>Login Page</h4>
<form action="shiro/login" method="post">
username:<input type="text" name="username"/>
<br/><br/>
password:<input type="password" name="password"/>
<br/><br/>
<input type="submit" value="Submit"/>
</form>
</body>
</html>

Shiro单Realm加密的更多相关文章

  1. Shiro笔记(四)Shiro的realm认证

    认证流程: 1.获取当前Subject.调用SecurityUtils.getSubject(); 2.测试当前用户是否已经被认证,即是否已经登录,调用Subject的isAurhenticated( ...

  2. shiro教程3(加密)

    加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容 概念 数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理, ...

  3. shiro双realm验证

    假设现在有这样一种需求:存在两张表user和admin,分别记录普通用户和管理员的信息.并且现在要实现普通用户和管理员的分开登录,即需要两个Realm——UserRealm和AdminRealm,分别 ...

  4. shiro多realm验证之——shiro实现不同身份使用不同Realm进行验证(转)

    转自: http://blog.csdn.net/xiangwanpeng/article/details/54802509 (使用特定的realm实现特定的验证) 假设现在有这样一种需求:存在两张表 ...

  5. Shiro多Realm验证

    在单Realm的基础上,进行如下内容的修改 原有的ShiroRealm.java: package com.atguigu.shiro.realms; import org.apache.shiro. ...

  6. Shiro中Realm

    6.1 Realm [2.5 Realm]及[3.5 Authorizer]部分都已经详细介绍过Realm了,接下来再来看一下一般真实环境下的Realm如何实现. 1.定义实体及关系   即用户-角色 ...

  7. springboot shiro 多realm配置认证、授权

    shiro进行登录认证和权限管理的实现.其中需求涉及使用两个角色分别是:门店,公司.现在要两者实现分开登录.即需要两个Realm——MyShiroRealmSHOP和MyShiroRealmCOMPA ...

  8. shiro盐值加密并验证

    在数据表中存的密码不应该是123456,而应该是123456加密之后的字符串,而且还要求这个加密算法是不可逆的,即由加密后的字符串不能反推回来原来的密码,如果能反推回来那这个加密是没有意义的.著名的加 ...

  9. shiro自定义Realm

    1.1 自定义Realm 上边的程序使用的是shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm. ...

随机推荐

  1. 链表栈C语言实现

    #ifndef LINKSTACK_H_INCLUDED #define LINKSTACK_H_INCLUDED #include <stdlib.h> #include <std ...

  2. 让socket端口不被子进程继承

    有两个程序A,B A绑定端口8000,然后用system函数启动程序B,然后再把A杀掉,这个时候再重启A的时候会发现绑定端口失败, 原因是端口被B绑定了,实际上是被继承了,为了避免这种情况,需要对主s ...

  3. leetcode-easy-others-191. Number of 1 Bits-NO

    mycode  不会... 输入是二进制....我还以为十进制.... 00000001011 = 11 题意: 编写一个将(无符号)整数作为输入的函数,并返回该数字二进制表示中等于1的位数.例如:输 ...

  4. SAEJ1757-1-2015(一)

    SURFACE VEHICLE STANDARD           表面车辆标准 Standard Metrology for Vehicular Displays             车载显示 ...

  5. Anaconda 32在windows下安装gensim

    安装Anaconda 2.4以后运行corpora.MmCorpus.serialize的时候出错 换了Anaconda 2.1以后没问题了 原因:Anaconda 2.4的numpy是1.10.1版 ...

  6. Promise.then链式调用

    let a = new Promise((resolve,reject)=>{ resolve(1) }).then((r)=>{console.log(r)}).then(()=> ...

  7. JMeter强大的性能测试工具

    JMeter强大的性能测试工具,可模拟服务器负载,进行性能测试 配合badboy采集请求数据.

  8. 爬虫六之爬取猫眼电影top100

    该爬虫比较简单,代码放在github上 https://github.com/GhostSteven/Crawler/tree/master/maoyantop100

  9. python基础之字符串常用方法

    str常用命令:字符操作:.capitalize() .upper() .lower() .title() .swapcase()判断:.startwith() .endwith() .isalnum ...

  10. vue调用组件,组件回调给data中的数组赋值,报错Invalid prop type check failed for prop value. Expecte

    报错信息: 代码信息:调用一个tree组件,选择一些信息 <componentsTree ref="typeTreeComponent" @treeCheck="t ...