1.下载shiro
http://shiro.apache.org/download.html
2.shiro架构的核心内容介绍
①Subject:应用代码直接交互的对象是 Subject,与 Subject 的所有交互都会委托给 SecurityManager;
②SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager 交互;且其管理着所有 Subject
③Realm:Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色/权限进行验证用户是否能进行操作.
3.引入shiro依赖
<properties>
<shiro.version>1.2.3</shiro.version>
</properties>
<!-- 引入ehcache的依赖 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.6</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
<!--Apache Shiro所需的jar包 start -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!--Apache Shiro所需的jar包 end -->
4.首先在web.xml中加入shiro过滤器
<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>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
5.添加shiro与spring集成的配置文件application-shiro.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-3.0.xsd">
<!-- shiro的核心 所有安全操作都将通过securityManager来处理 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 缓存管理器 -->
<property name="cacheManager" ref="cacheManager" />
<!-- session模式 native本地 http网络 -->
<!-- <property name="sessionMode" value="native" /> -->
<!-- realm 获取安全数据(如用户、角色、权限) -->
<property name="realm" ref="jdbcRealm" />
</bean>
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.MemorySessionDAO"/>
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionDAO" ref="sessionDAO"></property>
</bean>
<!-- 缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
</bean>
<!-- 查询安全数据(用户,角色,权限等) -->
<bean id="jdbcRealm" class="com.plat.shiro.CustomJdbcRealm">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 开启查询权限 -->
<property name="permissionsLookupEnabled" value="true"></property>
<!-- 授权登录sql -->
<property name="authenticationQuery"
value="SELECT su.password FROM sys_user su WHERE su.account = ? and su.is_use =1"></property>
<!-- 查询角色sql -->
<property name="userRolesQuery"
value="SELECT sr.rkey FROM sys_role sr,sys_user su,sys_user_role sur WHERE su.user_code = sur.user_code and sur.role_code = sr.rcode and su.account=?"></property>
<!-- 查询权限sql -->
<property name="permissionsQuery"
value="SELECT sp.pkey FROM sys_permission sp ,sys_role sr,sys_role_permission srp WHERE sr.rcode = srp.role_code and sp.pcode = srp.permission_code and sr.rkey=?"></property>
</bean>
<!-- 将shiro bean的生命周期交给spring管理 -->
<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>
<!-- 与web.xml中shiro过滤器的名字必须相同-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/index" />
<!-- 未授权跳转地址 -->
<!-- <property name="unauthorizedUrl" value="/s/unauthorized" /> -->
<!--
? 匹配任意一个字符 /login? /login1 /logina
* 匹配任意字符 /login* /login123 /loginabc123
/** 匹配任意地址 /login/** /login/xxx/xxx/xxx
shiro 过滤器
anon 无需授权即可访问
authc 必须登录才能访问,不包括记住我登录
user 授权即可访问,包括记住我登录
logout 退出拦截器
-->
<property name="filterChainDefinitions">
<value>
/**/*login*/**=anon
/resources/**=anon
/**/*logout*/**=logout
/**=user
</value>
</property>
</bean>
</beans>
6.缓存管理器ehcache.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- <diskStore path="G:\\shopCache" /> -->
<!-- 如果缓存内存溢出,则存储到储存空间 -->
<diskStore path="java.io.tmpdir" />
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false" />
<!--配置自定义缓存 maxElementsInMemory:缓存中允许创建的最大对象数
eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。 timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前,
两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,
如果该值是 0 就意味着元素可以停顿无穷长的时间。 timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值, 这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
overflowToDisk:内存不足时,是否启用磁盘缓存。 memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。
FIFO 先进先出策略,此算法已被淘汰 LRU 距离访问最远的先被踢(时间策略),会忽略访问频率 LFU 最未使用算法(频率优先) 会忽略访问先后时间
-->
<cache name="shopCache" maxElementsInMemory="10000" eternal="true"
overflowToDisk="false" timeToIdleSeconds="0" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU" />
</ehcache>
7.自定义realm
package com.plat.shiro;
import javax.annotation.Resource;
import org.apache.shiro.SecurityUtils;
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.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.session.Session;
import com.plat.commons.model.SysUser;
import com.plat.service.SysUserService;
public class CustomJdbcRealm extends JdbcRealm {
@Resource
private SysUserService sysUserService;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("------doGetAuthenticationInfo-------");
/**
* js --> sysUser/login --> Subject.login(UsernamepasswordToken) --> CustomJdbcRealm
*/
UsernamePasswordToken usernamePasswordToken =(UsernamePasswordToken) token;
//获取登录账号
String account=usernamePasswordToken.getUsername();
SysUser sysUser=sysUserService.findByAccount(account);
if(sysUser==null) {
throw new UnknownAccountException();
}
//判断用户名和密码是否正确
if(!sysUser.getPassword().equals(new String (usernamePasswordToken.getPassword()))) {
throw new IncorrectCredentialsException();
}
//判断用户是否被禁用
if(sysUser.getIsUse()==0) {
throw new LockedAccountException();
}
//用户登录成功,将用户保存到session中
Session session= SecurityUtils.getSubject().getSession();
session.setAttribute("sysUser", sysUser);
//参数1:Subject.getPrincipal()获取到的就是参数一传入的内容
//参数2:shiro进行用户信息比对的时候使用的密码
//参数3:realm名称,getName方法自动生成一个realm的名称
return new SimpleAuthenticationInfo(account,sysUser.getPassword(),getName());
}
}
8.登录方法
@RequestMapping("/login")
@ResponseBody
public String login(String account,String password) {
//返回信息
String message = "";
if(!isNotNull(account)) {
message="登录账号不能为空";
}
if(message=="" && !isNotNull(password)) {
message="登录密码不能为空";
}
//登录
if(!isNotNull(message)) {
Subject currentUser=SecurityUtils.getSubject();
//加密密码
String md5Password = DigestUtil.hmacSign(password, DigestUtil.digest("heheda"));
UsernamePasswordToken upToken=
new UsernamePasswordToken(account,md5Password);
//记住我
//upToken.setRememberMe(true);
try {
//进入shiro的登录,此时将进入自定义的realm中进行安全验证
currentUser.login(upToken);
message="success";
}catch (UnknownAccountException uae) {
message="账号不存在";
}catch (IncorrectCredentialsException ice) {
message="用户名或者密码错误";
}catch (LockedAccountException lae) {
message="账号已被锁定";
} catch (AuthenticationException e) {
message="登录异常,请稍后再试!!";
e.printStackTrace();
}
}
return message;
}
9.MD5
package com.plat.commons.util;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class DigestUtil {
private static String encodingCharset = "UTF-8";
/**
* @param aValue
* @param aKey
* @return
*/
public static String hmacSign(String aValue, String aKey) {
byte k_ipad[] = new byte[64];
byte k_opad[] = new byte[64];
byte keyb[];
byte value[];
try {
keyb = aKey.getBytes(encodingCharset);
value = aValue.getBytes(encodingCharset);
} catch (UnsupportedEncodingException e) {
keyb = aKey.getBytes();
value = aValue.getBytes();
}
Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);
Arrays.fill(k_opad, keyb.length, 64, (byte) 92);
for (int i = 0; i < keyb.length; i++) {
k_ipad[i] = (byte) (keyb[i] ^ 0x36);
k_opad[i] = (byte) (keyb[i] ^ 0x5c);
}
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
return null;
}
md.update(k_ipad);
md.update(value);
byte dg[] = md.digest();
md.reset();
md.update(k_opad);
md.update(dg, 0, 16);
dg = md.digest();
return toHex(dg);
}
public static String toHex(byte input[]) {
if (input == null)
return null;
StringBuffer output = new StringBuffer(input.length * 2);
for (int i = 0; i < input.length; i++) {
int current = input[i] & 0xff;
if (current < 16)
output.append("0");
output.append(Integer.toString(current, 16));
}
return output.toString();
}
/**
*
* @param args
* @param key
* @return
*/
public static String getHmac(String[] args, String key) {
if (args == null || args.length == 0) {
return (null);
}
StringBuffer str = new StringBuffer();
for (int i = 0; i < args.length; i++) {
str.append(args[i]);
}
return (hmacSign(str.toString(), key));
}
/**
* @param aValue
* @return
*/
public static String digest(String aValue) {
aValue = aValue.trim();
byte value[];
try {
value = aValue.getBytes(encodingCharset);
} catch (UnsupportedEncodingException e) {
value = aValue.getBytes();
}
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
return toHex(md.digest(value));
}
public static void main(String[] args) {
// 参数1: 明文(要加密的数据) 参数2: 密钥
System.out.println(DigestUtil.hmacSign("11111", "dddd"));
}
}