160720、SSM-Shiro使用详解
前言
相比有做过企业级开发的童鞋应该都有做过权限安全之类的功能吧,最先开始我采用的是建用户表,角色表,权限表,之后在拦截器中对每一个请求进行拦截,再到数据库中进行查询看当前用户是否有该权限,这样的设计能满足大多数中小型系统的需求。不过这篇所介绍的Shiro能满足之前的所有需求,并且使用简单,安全性高,而且现在越来越的多企业都在使用Shiro,这应该是一个收入的你的技能库。
创建自定义MyRealm类
有关Shiro的基础知识我这里就不过多介绍了,直接来干货,到最后会整合Spring来进行权限验证。
首先在使用Shiro的时候我们要考虑在什么样的环境下使用:
- 登录的验证
- 对指定角色的验证
- 对URL的验证
基本上我们也就这三个需求,所以同时我们也需要三个方法:
1. findUserByUserName(String username)根据username查询用户,之后Shiro会根据查询出来的User的密码来和提交上来的密码进行比对。
2. findRoles(String username)根据username查询该用户的所有角色,用于角色验证。
3. findPermissions(String username)根据username查询他所拥有的权限信息,用于权限判断。
下面我贴一下我的mapper代码(PS:该项目依然是基于之前的SSM,不太清楚整合的请看SSM一)。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.crossoverJie.dao.T_userDao" >
<resultMap id="BaseResultMap" type="com.crossoverJie.pojo.T_user" >
<result property="id" column="id"/>
<result property="userName" column="userName"/>
<result property="password" column="password"/>
<result property="roleId" column="roleId"/>
</resultMap>
<sql id="Base_Column_List" >
id, username, password,roleId
</sql>
<select id="findUserByUsername" parameterType="String" resultMap="BaseResultMap">
select <include refid="Base_Column_List"/>
from t_user where userName=#{userName}
</select>
<select id="findRoles" parameterType="String" resultType="String">
select r.roleName from t_user u,t_role r where u.roleId=r.id and u.userName=#{userName}
</select>
<select id="findPermissions" parameterType="String" resultType="String">
select p.permissionName from t_user u,t_role r,t_permission p
where u.roleId=r.id and p.roleId=r.id and u.userName=#{userName}
</select>
</mapper>
很简单只有三个方法,分别对应上面所说的三个方法。对sql稍微熟悉点的童鞋应该都能看懂,不太清楚就拷到数据库中执行一下就行了,数据库的Sql也在我的github上。实体类就比较简单了,就只有四个字段以及get,set方法。我就这里就不贴了,具体可以去github上fork我的源码。
现在就需要创建自定义的MyRealm类,这个还是比较重要的。继承至Shiro的AuthorizingRealm类,用于处理自己的验证逻辑,下面贴一下我的代码:
package com.crossoverJie.shiro;
import com.crossoverJie.pojo.T_user;
import com.crossoverJie.service.T_userService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import javax.annotation.Resource;
import java.util.Set;
/** * Created with IDEA * Created by ${jie.chen} on 2016/7/14. * Shiro自定义域 */
public class MyRealm extends AuthorizingRealm {
@Resource
private T_userService t_userService;
/** * 用于的权限的认证。 * @param principalCollection * @return */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = principalCollection.getPrimaryPrincipal().toString() ;
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo() ;
Set<String> roleName = t_userService.findRoles(username) ;
Set<String> permissions = t_userService.findPermissions(username) ;
info.setRoles(roleName);
info.setStringPermissions(permissions);
return info;
}
/** * 首先执行这个登录验证 * @param token * @return * @throws AuthenticationException */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
//获取用户账号
String username = token.getPrincipal().toString() ;
T_user user = t_userService.findUserByUsername(username) ;
if (user != null){
//将查询到的用户账号和密码存放到 authenticationInfo用于后面的权限判断。第三个参数传入realName。
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),
"a") ;
return authenticationInfo ;
}else{
return null ;
}
}
}
继承AuthorizingRealm类之后就需要覆写它的两个方法,doGetAuthorizationInfo,doGetAuthenticationInfo,这两个方法的作用我都有写注释,逻辑也比较简单。doGetAuthenticationInfo是用于登录验证的,在登录的时候需要将数据封装到Shiro的一个token中,执行shiro的login()方法,之后只要我们将MyRealm这个类配置到Spring中,登录的时候Shiro就会自动的调用doGetAuthenticationInfo()方法进行验证。
哦对了,忘了贴下登录的Controller了:
package com.crossoverJie.controller;
import com.crossoverJie.pojo.T_user;
import com.crossoverJie.service.T_userService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
/** * Created with IDEA * Created by ${jie.chen} on 2016/7/14. * 后台Controller */
@Controller
@RequestMapping("/")
public class T_userController {
@Resource
private T_userService t_userService ;
@RequestMapping("/loginAdmin")
public String login(T_user user, Model model){
Subject subject = SecurityUtils.getSubject() ;
UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(),user.getPassword()) ;
try {
subject.login(token);
return "admin" ;
}catch (Exception e){
//这里将异常打印关闭是因为如果登录失败的话会自动抛异常
// e.printStackTrace();
model.addAttribute("error","用户名或密码错误") ;
return "../../login" ;
}
}
@RequestMapping("/admin")
public String admin(){
return "admin";
}
@RequestMapping("/student")
public String student(){
return "admin" ;
}
@RequestMapping("/teacher")
public String teacher(){
return "admin" ;
}
}
主要就是login()方法。逻辑比较简单,只是登录验证的时候不是像之前那样直接查询数据库然后返回是否有用户了,而是调用subject的login()方法,就是我上面提到的,调用login()方法时Shiro会自动调用我们自定义的MyRealm类中的doGetAuthenticationInfo()方法进行验证的,验证逻辑是先根据用户名查询用户,如果查询到的话再将查询到的用户名和密码放到SimpleAuthenticationInfo对象中,Shiro会自动根据用户输入的密码和查询到的密码进行匹配,如果匹配不上就会抛出异常,匹配上之后就会执行doGetAuthorizationInfo()进行相应的权限验证。doGetAuthorizationInfo()方法的处理逻辑也比较简单,根据用户名获取到他所拥有的角色以及权限,然后赋值到SimpleAuthorizationInfo对象中即可,Shiro就会按照我们配置的XX角色对应XX权限来进行判断,这个配置在下面的整合中会讲到。
整合Spring
接下来应该是大家比较关系的一步:整合Spring。
我是在之前的Spring SpringMVC Mybatis的基础上进行整合的。
web.xml配置
首先我们需要在web.xml进行配置Shiro的过滤器。
我只贴Shiro部分的,其余的和之前配置是一样的。
<!-- shiro过滤器定义 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->
<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>
配置还是比较简单的,这样会过滤所有的请求。
之后我们还需要在Spring中配置一个shiroFilter的bean。
spring-mybatis.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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- 自动扫描 -->
<context:component-scan base-package="com.crossoverJie" />
<!-- 引入配置文件 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 指定连接数据库的驱动 -->
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="3" />
<property name="minIdle" value="3" />
<property name="maxActive" value="20" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->
<property name="filters" value="stat" />
</bean>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:mapping/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.crossoverJie.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置自定义Realm -->
<bean id="myRealm" class="com.crossoverJie.shiro.MyRealm"/>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
</bean>
<!-- Shiro过滤器 核心-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 身份认证失败,则跳转到登录页面的配置 -->
<property name="loginUrl" value="/login.jsp"/>
<!-- 权限认证失败,则跳转到指定页面 -->
<property name="unauthorizedUrl" value="/nopower.jsp"/>
<!-- Shiro连接约束配置,即过滤链的定义 -->
<property name="filterChainDefinitions">
<value>
<!--anon 表示匿名访问,不需要认证以及授权-->
/loginAdmin=anon
<!--authc表示需要认证 没有进行身份认证是不能进行访问的-->
/admin*=authc
/student=roles[teacher]
/teacher=perms["user:create"]
</value>
</property>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<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>
</beans>
在这里我们配置了上文中所提到的自定义myRealm,这样Shiro就可以按照我们自定义的逻辑来进行权限验证了。其余的都比较简单,看注释应该都能明白。
着重讲解一下:
<property name="filterChainDefinitions">
<value>
<!--anon 表示匿名访问,不需要认证以及授权-->
/loginAdmin=anon
<!--authc表示需要认证 没有进行身份认证是不能进行访问的-->
/admin*=authc
/student=roles[teacher]
/teacher=perms["user:create"]
</value>
</property>
- /loginAdmin=anon的意思的意思是,发起/loginAdmin这个请求是不需要进行身份认证的,这个请求在这次项目中是一个登录请求,一般对于这样的请求都是不需要身份认证的。
- /admin*=authc表示 /admin,/admin1,/admin2这样的请求都是需要进行身份认证的,不然是不能访问的。
- /student=roles[teacher]表示访问/student请求的用户必须是
teacher角色,不然是不能进行访问的。 - /teacher=perms[“user:create”]表示访问/teacher请求是需要当前用户具有
user:create权限才能进行访问的。
更多相关权限过滤的资料可以访问shiro的官方介绍:传送门
使用Shiro标签库
Shiro还有着强大标签库,可以在前端帮我获取信息和做判断。
我贴一下我这里登录完成之后显示的界面:
<%-- Created by IntelliJ IDEA. User: Administrator Date: 2016/7/14 Time: 13:17 To change this template use File | Settings | File Templates. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
<title>后台</title>
</head>
<body>
<shiro:hasRole name="admin">
这是admin角色登录:<shiro:principal></shiro:principal>
</shiro:hasRole>
<shiro:hasPermission name="user:create">
有user:create权限信息
</shiro:hasPermission>
<br>
登录成功
</body>
</html>
要想使用Shiro标签,只需要引入一下标签即可:<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
其实英语稍微好点的童鞋应该都能看懂。下面我大概介绍下一些标签的用法:
- 具有admin角色才会显示标签内的信息。
- 获取用户信息。默认调用Subject.getPrincipal()获取,即 Primary Principal。
- 用户拥有user:create这个权限才回显示标签内的信息。
更多的标签可以查看官网:传送门
整体测试

这是我的测试数据。
首先来验证一下登录:
先输入一个错误的账号和密码:
接下来输入一个正确的:
可以看到我登录的用户是crossoverJie他是有admin的角色,并且拥有user:*(ps:系统数据详见上面的数据库截图)的权限,所以在这里:
<shiro:hasRole name="admin">
这是admin角色登录:<shiro:principal></shiro:principal>
</shiro:hasRole>
<shiro:hasPermission name="user:create">
有user:create权限信息
</shiro:hasPermission>
是能显示出标签内的信息,并把用户信息也显示出来了。
接着我们来访问一下/student这个请求,因为在Spring的配置文件中:
<property name="filterChainDefinitions">
<value>
<!--anon 表示匿名访问,不需要认证以及授权-->
/loginAdmin=anon
<!--authc表示需要认证 没有进行身份认证是不能进行访问的-->
/admin*=authc
/student=roles[teacher]
/teacher=perms["user:create"]
</value>
</property>
只有teacher角色才能访问/student这个请求的:
果然,Shiro做了安全控制是不能进行访问的。
然后我们换aaa用户登录,他正好是teacher角色,看能不能访问/student。
果然是能访问的。
因为我在控制器里访问/student返回的是同一个界面所以看到的还是这个界面。
@RequestMapping("/teacher")
public String teacher(){
return "admin" ;
}
并且没有显示之前Shiro标签内的内容。
其他的我就不测了,大家可以自己在数据库里加一些数据,或者是改下拦截的权限多试试,这样对Shiro的理解就会更加深刻。
MD5加密
Shiro还封装了一个我认为非常不错的功能,那就是MD5加密,代码如下:
package com.crossoverJie.shiro;
import org.apache.shiro.crypto.hash.Md5Hash;
/** * Created with IDEA * 基于Shiro的MD5加密 * Created by ${jie.chen} on 2016/7/13. */
public class MD5Util {
public static String md5(String str,String salt){
return new Md5Hash(str,salt).toString() ;
}
public static void main(String[] args) {
String md5 = md5("abc123","crossoverjie") ;
System.out.println(md5);
}
}
代码非常简单,只需要调用Md5Hash(str,salt)方法即可,这里多了一个参数,第一个参数不用多解释,是需要加密的字符串。第二个参数salt中文翻译叫盐,加密的时候我们传一个字符串进去,只要这个salt不被泄露出去,那原则上加密之后是无法被解密的,在存用户密码的时候可以使用,感觉还是非常屌的。
总结
以上就是Shiro实际使用的案例,将的比较初略,但是关于Shiro的核心东西都在里面了。大家可以去我的github上下载源码,只要按照我给的数据库就没有问题,项目跑起来之后试着改下里面的东西可以加深对Shiro的理解。
项目地址:https://github.com/crossoverJie/SSM.git
个人博客地址:http://crossoverjie.top。
GitHub地址:https://github.com/crossoverJie。
160720、SSM-Shiro使用详解的更多相关文章
- 转:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法、shiro认证与shiro授权
原文地址:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法.shiro认证与shiro授权 以下是部分内容,具体见原文. shiro介绍 什么是shiro shiro是Apache ...
- Shiro认证详解
Shiro shiro是一个java的安全框架 官网地址 http://shiro.apache.org/ 目录 Shiro Shiro综述 过滤器 注解 整合Shiro 1. 配置SecurityM ...
- Shiro学习详解
1.Shiro基本架构 一.什么是Shiro Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户“登录”: 授权 ...
- shiro过滤器详解分析
(原) shiro最核心的2个操作,一个是登录的实现,一就是过滤器了.登录有时间再补录说明,这里分析下shiro过滤器怎样玩的. 1.目标 这里会按如下顺序逐一看其实原理,并尽量找出其出处. 先看一下 ...
- JavaWeb开发SSM框架搭建详解
1.需要用到的jar包:由于很多的jar包不好下载,我直接上传到百度网盘: 很多,而且不好下载,我已经整理好好了: 链接:https://pan.baidu.com/s/1iIFprmstp86uKz ...
- spring与shiro配置详解
1.加入shiro相关依赖的jar包 pom.xml部分内容如下: <dependency> <groupId>org.apache.shiro</groupId> ...
- shiro学习详解(开篇)
一.前言 要开始接触公司另外一个项目了,RX和我说了下整个项目框架的结构,其中提到权限的控制是通过shiro来处理的,对我而言又是一个全新的知识点,于是今天花了一点时间去学习shiro的使用,看了好几 ...
- Maven搭建Spring+SpringMVC+Mybatis+Shiro项目详解
一. 环境搭建: 1. 开发工具:myeclipse 2014 / IDEA: 2. maven管理版本:apache-maven-3.0+: 3. jdk 1.7.0+4. Tomcat8.0 二: ...
- SpringMVC+MyBatis+Shiro 配置文件详解
1.web.xml文件的配置 <?xml version="1.0" encoding="UTF-8"?> <web-app version= ...
随机推荐
- MVC中Area的另一种用法
[摘要]本文只是为一行代码而分享 context.MapRoute("API", "api/{controller}/{action}", new { }, n ...
- IIS6.0应用程序池回收(转载)
这段时间公司的程序经常出现问题,然后整个应用程序就不能访问了,我们的服务器版本:window 2003 SP1,IIS6.0,没有安装Microsoft Visual Studio .NET . 问题 ...
- [elk]es增删改查最佳实战
PUT app01 GET app01/_settings GET _all/_settings PUT app01/_settings { "number_of_replicas" ...
- web ul li
<html> <head> <style type="text/css"> ul{float:right} ul li{float:left; ...
- 在 Linux 客户端配置基于 Kerberos 身份验证的 NFS 服务器
在这篇文章中我们会介绍配置基于 Kerberos 身份验证的 NFS 共享的整个流程.假设你已经配置好了一个 NFS 服务器和一个客户端.如果还没有,可以参考 安装和配置 NFS 服务器[2] - 它 ...
- hdu 1022 Train Problem
Train Problem I Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)T ...
- 跟着百度学PHP[17]-PHP扩展CURL的POST传输数据
如果是GET的话就不必那么多设置.但是基本需要用到POST就需要用到以下的几个设置选项. <?php $username = "admin"; $password = &qu ...
- Action方法调用
一.Action访问路径 Action的访问路径是由struts.xml文件中配置的Action所在包的命名空间,Action的名字和常struts.action.extension共同决定的 例如: ...
- JVM Specification 9th Edition (3) Chapter 2. The Structure of the Java Virtual Machine
Chapter 2. The Structure of the Java Virtual Machine 内容列表 2.1. The class File Format (class文件的格式) 2. ...
- Java中模拟POST上传文件
/** * * @param url 请求URL * @param filePath 本地文件地址 * @return */ public static String upload(String ur ...