Shiro是apache旗下的一款轻量级的Java安全框架,它能够提供以下服务:html
Authentication(认证)java
Authorization(受权)mysql
Session Management(会话管理)web
Cryptography(加密)spring
接下来咱们来一块儿进入shiro的世界吧sql
<!-- Shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.3</version> </dependency>
[users] shiro = 201314
package com.simple.shiro; import org.apache.log4j.Logger; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; /** * @author xiaobianchen * @version 1.0 2016/4/19 */ public class HelloShiro { private static final Logger logger = Logger.getLogger(HelloShiro.class); public static void main(String[] args) { Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); //获取当前用户 Subject currentUser = SecurityUtils.getSubject(); //登陆 UsernamePasswordToken token = new UsernamePasswordToken("shiro", "201314"); try { currentUser.login(token); } catch (AuthenticationException e) { logger.info("login failure!"); return; } logger.info("login success! Hello " + currentUser.getPrincipal()); //注销 currentUser.logout(); } }
咱们分析一下这个 HelloShiro 吧: 数据库
须要读取 classpath 下的 shiro.ini 配置文件,并经过工厂类建立 SecurityManager 对象,最终将其放入 SecurityUtils 中,供 Shiro 框架随时获取。apache
一样经过 SecurityUtils 类获取 Subject 对象,其实就是当前用户,只不过在 Shiro 的世界里优雅地将其称为 Subject(主体)。缓存
首先使用一个 Username 与 Password,来建立一个 UsernamePasswordToken 对象,而后咱们经过这个 Token 对象调用 Subject 对象的 login 方法,让 Shiro 进行用户身份认证。安全
当登陆失败时,您可使用 AuthenticationException 来捕获这个异常;当登陆成功时,您能够调用 Subject 对象的 getPrincipal 方法来获取 Username,此时 Shiro 已经为您建立了一个 Session。
最后仍是经过 Subject 对象的 logout 方法来注销本次 Session。
shiro的内部调用流程以下:
Shiro官方提供的Web模块-shiro-web
你只须要在pom.xml文件中添加以下配置:
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.3</version> </dependency>
而后在web.xml文件中添加Listener与一个Filter
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <listener> <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class> </listener> <filter> <filter-name>ShiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> </filter> <filter-mapping> <filter-name>ShiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
EnvironmentLoaderListener这个监听器是用来初始化SecurityManager的,而且经过ShiroFilter来完成验证和受权的
在数据库中创建以下面的表
在shiro.ini配置:
[main] authc.loginUrl=/login ds = org.apache.commons.dbcp.BasicDataSource ds.driverClassName = com.mysql.jdbc.Driver ds.url = jdbc:mysql://localhost:3306/test ds.username = root ds.password = root passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm jdbcRealm.dataSource = $ds jdbcRealm.authenticationQuery = select password from user where username = ? jdbcRealm.userRolesQuery = select r.role_name from user u, user_role ur, role r where u.id = ur.user_id and r.id = ur.role_id and u.username = ? jdbcRealm.permissionsQuery = select p.permission_name from role r, role_permission rp, permission p where r.id = rp.role_id and p.id = rp.permission_id and r.role_name = ? jdbcRealm.permissionsLookupEnabled = true jdbcRealm.credentialsMatcher = $passwordMatcher securityManager.realms = $jdbcRealm cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager securityManager.cacheManager = $cacheManager [urls] / = anon /space/** = authc
对以上配置解释以下:
首先,在 [main] 片断中,咱们定义了一个“authc.loginUrl=/login”,用于配置当须要认证时须要跳转的 URL 地址,这里表示重定向到 /login 请求,经过 Servlet 映射后可定位到 login 页面。
而后,定义了一个 DBCP 的 DataSource,用于获取 JDBC 数据库链接。
而后,定义 JdbcRealm 并指定 DataSource,经过配置如下几条 SQL 来完成认证与受权:
authenticationQuery:该 SQL 语句用于提供身份认证,即经过 username 查询 password。
userRolesQuery:该 SQL 语句用于提供基于角色的受权验证(属于粗粒度级别),即经过 username 查询 role_name。
permissionQuery:该 SQL 语句用于提供基于权限的受权验证(属于细粒度级别),即经过 role_name 查询 permission_name,此时须要开启 permissionsLookupEnabled 开关,默认是关闭的。
最后,在 [urls] 片断中,咱们定义了一系列的 URL 过滤规则,Shiro 已经提供了一些默认的 Filter(过滤器),便于咱们随时使用,固然您也能够扩展其它的过滤器。就本例配置而言,解释以下:
/ = anon:对于“/”请求(首页)能够匿名访问的。
/space/** = authc:对于以“/space/”开头的请求,均由 authc 过滤器处理,也就是完成身份认证操做。
每次认证都须要与数据库打交道,因此基于性能的考虑 在这里咱们配置了cacheManager
那么对于咱们新建一个index.jsp页面
<%@ page pageEncoding="UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> <html> <head> <title>首页</title> </head> <body> <h1>首页</h1> <shiro:guest> <p>身份:游客</p> <a href="<c:url value="/login"/>">登陆</a> <a href="<c:url value="/register"/>">注册</a> </shiro:guest> <shiro:user> <p>身份:<shiro:principal/></p> <a href="<c:url value="/space"/>">空间</a> <a href="<c:url value="/logout"/>">退出</a> </shiro:user> </body> </html>
基于servlet服务器的代码:
@WebServlet("/login") public class LoginServlet extends HttpServlet { private UserService userService = new UserServiceImpl(); @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取表单数据 String username = request.getParameter("username"); String password = request.getParameter("password"); boolean isRememberMe = request.getParameter("rememberMe") != null; // 调用登陆服务 try { userService.login(username, password, isRememberMe); } catch (LoginException e) { request.setAttribute("exception", e.getName()); return; } // 重定向到空间页面 response.redirect("/index") } }
关于shiro-web目前就介绍这么多了
spring与shiro的集成 首先须要在pom.xml中添加依赖:
<!-- Shiro Spring--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.3</version> </dependency>
web.xml中作以下配置:
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- 读取配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:conf/spring-mybatis.xml classpath:conf/spring-shiro.xml </param-value> </context-param> <!--字符集过滤器--> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class> </listener> <filter> <filter-name>ShiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> </filter> <filter-mapping> <filter-name>ShiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--springmvc 核心配置--> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <description>SpringContext</description> <param-name>contextConfigLocation</param-name> <param-value>classpath:conf/mvc-dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 配置主页 --> <welcome-file-list> <welcome-file>/WEB-INF/jsp/login.jsp</welcome-file> </welcome-file-list> <!-- 配置session超时时间,单位分钟 --> <session-config> <session-timeout>30</session-timeout> </session-config> <!--配置错误页面--> <error-page> <error-code>404</error-code> <location>/WEB-INF/jsp/error/404.jsp</location> </error-page> </web-app>
最重要的spring-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.xsd"> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login"/> <property name="filterChainDefinitions"> <value> / = anon /space/** = authc </value> </property> </bean> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="jdbcRealm"/> <property name="cacheManager" ref="cacheManager"/> </bean> <bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm"> <property name="dataSource" ref="dataSource"/> <property name="authenticationQuery" value="select password from user where username = ?"/> <property name="userRolesQuery" value="select r.role_name from user u, user_role ur, role r where u.id = ur.user_id and r.id = ur.role_id and u.username = ?"/> <property name="permissionsQuery" value="select p.permission_name from role r, role_permission rp, permission p where r.id = rp.role_id and p.id = rp.permission_id and r.role_name = ?"/> <property name="permissionsLookupEnabled" value="true"/> <property name="cacheManager" ref="cacheManager"/> <property name="credentialsMatcher" ref="passwordMatcher"/> </bean> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!--缓存--> <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"/> <!--密码加密--> <bean id="passwordMatcher" class="org.apache.shiro.authc.credential.PasswordMatcher"/> <bean id="passwordService" class="org.apache.shiro.authc.credential.DefaultPasswordService"/> </beans>
登陆页面建一个jsp页面:
<%@ page pageEncoding="UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> <html> <head> <title>登陆</title> </head> <body> <h1><a href="<c:url value="/"/>">首页</a> - 登陆</h1> <shiro:notAuthenticated> <form action="<c:url value="/signIn"/>" method="post"> <table> <tr> <td>用户名:</td> <td><input type="text" name="username"/></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"/></td> </tr> <tr> <td></td> <td> <label><input type="checkbox" name="rememberMe"/>记住我</label> </td> </tr> <tr> <td></td> <td> <button type="submit">登陆</button> </td> </tr> </table> </form> <c:if test="${requestScope['exception'] == 'LoginException'}"> <p>登陆失败!</p> </c:if> </shiro:notAuthenticated> <shiro:authenticated> <c:redirect url="/space"/> </shiro:authenticated> </body> </html>
控制器层:
@Controller public class LoginController { private static final Logger LOG = LoggerFactory.getLogger(LoginController.class); @RequestMapping(value = "/index",method = RequestMethod.GET) public String index(){ return "index"; } @Autowired private IUserService userService; @RequestMapping(value = "/signIn",method = RequestMethod.GET) public String signIn(){ return "signIn"; } @RequestMapping(value = "/signIn",method = RequestMethod.POST) public String submit(HttpServletRequest request) { // 获取表单数据 String username = request.getParameter("username"); String password = request.getParameter("password"); boolean isRememberMe = request.getParameter("rememberMe") != null; // 调用登陆服务 try { userService.login(username, password); } catch (LoginException e) { request.setAttribute("exception", e.getName()); return signIn(); } // 重定向到主页面 return "redirect:/index"; } }