这里讲shiro整合spring 实现登录验证 和权限拦截
首先pom文件添加依赖
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.3.2</version>
</dependency>
<!-- spring整合shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- ehcache-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
shiro要结合ehcache这个缓存框架 可以实现一些功能 比如限制用户登录密码错误次数 然后限制多久时间内,限制登录
接下来就来就是配置文件 首先web.xml
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>DelegatingFilterProxy</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这是配置一个shiro过滤器 将所有的请求都交给shiro处理 至于spring的配置我这里就不写了 和正常的配置一样
然后ehcache缓存框架需要一个配置文件 我这里贴出来 文件名就叫 ehcache.xml
1 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
2
3 <diskStore path="${webapp.root}"/>
4
5 <defaultCache
6 maxElementsInMemory="10000"
7 maxElementsOnDisk="0"
8 eternal="true"
9 overflowToDisk="true"
10 diskPersistent="false"
11 timeToIdleSeconds="0"
12 timeToLiveSeconds="0"
13 diskSpoolBufferSizeMB="50"
14 diskExpiryThreadIntervalSeconds="120"
15 memoryStoreEvictionPolicy="LFU"
16 />
17 </ehcache>
接下来就是spring整合shiro的配置文件
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 <!-- 配置ehCache缓存支持-->
8 <bean name="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
9 <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
10 </bean>
11
12
13 <!-- 自定义Realm-->
14 <bean name="userRealm" class="com.newer.web.shiro.MyRealm" >
15
16 <!--注入加密算法类 -->
17 <property name="credentialsMatcher">
18
19 <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
20 <!--加密算法名 -->
21 <property name="hashAlgorithmName" value="MD5"/>
22 <!--加密次数 -->
23 <property name="hashIterations" value="6"/>
24 </bean>
25
26 </property>
27
28
29 </bean>
30
31
32 <!-- shiro安全管理器-->
33 <bean name="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
34 <property name="cacheManager" ref="cacheManager"></property>
35 <property name="realm" ref="userRealm"></property>
36 </bean>
37
38
39 <!-- 管理shiro bean的生命周期 -->
40 <bean name="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
41
42
47
48 <!--shiro核心过滤器配置-->
49 <bean name="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
50 <property name="securityManager" ref="securityManager"/>
51 <property name="loginUrl" value="/login.jsp"></property> <!-- 没有认证 跳转的登录连接 -->
52 <property name="unauthorizedUrl" value="/unauthorized.jsp"></property> <!-- 没有访问权限 跳转的页面 -->
82 <!--这里使用 从数据库查询出来要拦截的url集合 注入到这个对象中 实现拦截 -->
83 <property name="filterChainDefinitionMap" ref="map"/>
86 </bean>
87
88
89 <!--实例工厂方法 将执行shiroFilterUtils类的build的方法 -->
90 <bean name="map" factory-bean="shiroFilterUtils" factory-method="build"/>
91
92 <!--这个类将从数据库中查询要拦截的url信息-->
93 <bean name="shiroFilterUtils" class="com.newer.util.ShiroFilterUtils"/>
97 </beans>
别的配置不说 shiro核心过滤器bean的 name的值 一定要与web.xml里面配置的shiro过滤器的名字一样 否则会报错
然后我这里是使用实例工厂方法 将一个存有要拦截的url的map集合 注入到ShiroFilterFactoryBean 中的filterChainDefinitionMap属性
这样当我们的请求经过shiro过滤器时 会根据我们配置的要拦截的url和使用相应的拦截器 对请求进行处理
现在把我这个工具类贴出来
/**
* 这个类会在 spring容器初始化bean时 从数据库将要拦截的url
* 查询出来 封装成集合 注入到ShiroFilterFactoryBean类的filterChainDefinitions属性中
* 这样实现对需要相应权限才能访问的url进行拦截 并由shiro验证是否具有相应权限
*/
public class ShiroFilterUtils {
public Map<String,String> build(){
Map<String,String> map= new LinkedHashMap<String, String>();
//添加固定要拦截的url
map.put("/user/login.do","anon");
map.put("/login.jsp","anon");
map.put("/**","authc");
return map;
}
}
这里讲一下 url和拦截器的语法
url使用的是 Ant风格
? 匹配任何单字符
* 匹配0或者任意数量的字符
** 匹配0或者更多的目录
拦截器常用有:
anon 匿名的 不需要认证 也可以访问
authc 必须认证才可以访问 没有认证会跳转登录链接
user 登录之后启用了记住我功能 就可以访问
roles 例如 roles[管理员] 表示必须具有管理员权限才可操作 如果是roles[管理员,经理] 表示要具有管理员和经理权限才可以访问
------------------------------------------
shiro会将请求的url按照顺序和map集合中的所有url匹配 如果匹配到了 不会再匹配后续url 所以要注意 map集合中要拦截的url的顺序
我这里就没有将要拦截的url 从数据库中查出 如果需要从数据集中查出 然后将要拦截的url作为key 要使用的拦截器作为value 存入这个map集合即可
权限管理就这么多 接下来说登录认证 贴出我的controller
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(String userName, String userPassword, ModelMap map){
//得到当前 Subject
Subject currentSubject= SecurityUtils.getSubject();
//创建用户名密码 token
UsernamePasswordToken token= new UsernamePasswordToken(userName,userPassword);
//记住当前用户 关掉浏览器 重开也能访问 不是authc 拦截器的url
token.setRememberMe(true);
try {
//执行登录方法
currentSubject.login(token);
}catch (IncorrectCredentialsException ie){
//将抛出的登录异常 存储 然后返回登录页面 显示给用户
map.put("loginMessage","用户名或密码错误!!!");
return "forward:/login.jsp";
}catch (AuthenticationException ae){ //所有异常的父类
map.put("loginMessage","未知错误 请联系管理员");
return "forward:/login.jsp";
}
return "success";
}
@RequestMapping("/logout")
public String logout(){
//得到当前 Subject
Subject currentSubject= SecurityUtils.getSubject();
//注销当前 Subject
currentSubject.logout();
return "redirect:/login.jsp";
}
//这里面没有去写注册方法了 注册的时候 要将密码 通过这样加密后存入到数据库
public static void main(String[] args) {
//生成MD5加密的密码 参1:加密方式 ,参2:未加密的密码 参3:盐值 使用当前用户名 参4:加密次数
Object str= new SimpleHash("MD5","123456","zhangsan",6);
System.out.println("str = " + str);
}
}
当执行login()方法时 会调用我们在shiro配置文件中配置的自定义Realm类 login方法出现错误 会抛出异常 我们用catch可以捕获对应异常
接下来贴出我的Realm类
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private AuthorityService authorityService;
@Override //认证方法
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//将token强转成 用户密码token
UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken;
//根据传入的用户名 查询数据库 返回用户对象
User user=userService.queryByUserName(token.getUsername());
//没有查到用户 直接抛出异常
if(user==null){
throw new IncorrectCredentialsException();
}
//根据当前用户名 生成盐值 用于结合加密算法对密码进行加密
ByteSource salt=ByteSource.Util.bytes(token.getUsername());
//将用户名和密码 交由shiro认证 如果出错会抛出对应异常 参数1:用户名 参数2:用户密码 参3:盐值 参数4:当前Realm的名字
return new SimpleAuthenticationInfo(user.getUserName(),user.getUserPassword(),salt,this.getName());
}
@Override //授权方法
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获得用户名
String userName= (String) principalCollection.getPrimaryPrincipal();
//根据用户名 查询当前用户 拥有的权限
Set<String> roles=authorityService.queryByUserName(userName);
//将当前权限集合 返回交给shiro处理 权限认证
return new SimpleAuthorizationInfo(roles);
}
}
第二个授权方法 就是当请求需要对应权限的拦截器的url时 shiro会调用这个方法 然后我们根据当前用户名 查询出当前用户所拥有的权限
然后交给shiro 由他去比对当前用户是否拥有可以访问的资格 如果没有就会跳转到我们在配置文件中 shiro过滤器配置的unauthorizedUrl属性的值去
shiro的基本用法就这些 网上看了很多教程 都是各不相同 不像别的框架 教程都差不多 暂时总结到这里 后续有新的用法 再来加上吧