輕鬆帶你走進shiro的世界

1.10分鐘帶你輕鬆入門shiro

Shiro是apache旗下的一款輕量級的Java安全框架,它能夠提供以下服務:html

  • Authentication(認證)java

  • Authorization(受權)mysql

  • Session Management(會話管理)web

  • Cryptography(加密)spring


接下來咱們來一塊兒進入shiro的世界吧sql


第一步: 在pom.xml文件中添加如下依賴:

<!-- Shiro -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.2.3</version>
</dependency>


第二步: 在classpath文件下添加shiro.ini文件:

[users]
shiro = 201314


第三步:認識一下shiro的認證服務,寫一個main方法測試一下:

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 吧: 數據庫

  1. 須要讀取 classpath 下的 shiro.ini 配置文件,並經過工廠類建立 SecurityManager 對象,最終將其放入 SecurityUtils 中,供 Shiro 框架隨時獲取。apache

  2. 一樣經過 SecurityUtils 類獲取 Subject 對象,其實就是當前用戶,只不過在 Shiro 的世界裏優雅地將其稱爲 Subject(主體)。緩存

  3. 首先使用一個 Username 與 Password,來建立一個 UsernamePasswordToken 對象,而後咱們經過這個 Token 對象調用 Subject 對象的 login 方法,讓 Shiro 進行用戶身份認證。安全

  4. 當登陸失敗時,您可使用 AuthenticationException 來捕獲這個異常;當登陸成功時,您能夠調用 Subject 對象的 getPrincipal 方法來獲取 Username,此時 Shiro 已經爲您建立了一個 Session。

  5. 最後仍是經過 Subject 對象的 logout 方法來註銷本次 Session。


shiro的內部調用流程以下:


2.Web開發中使用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目前就介紹這麼多了


3.Spring與shiro的集成

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";
    }
}
相關文章
相關標籤/搜索