1. 程式人生 > >springmvc+shiro驗證嵌入方法介紹,學習筆記(一)

springmvc+shiro驗證嵌入方法介紹,學習筆記(一)

1.新增shiro框架包

<!--shiro許可權管理-->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>${shiro.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-ehcache</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>

2.在web.xml中新增過濾器,意思是url被該過濾器攔截做許可權驗證,驗證通過才給到controller。  

<!-- 配置  Shiro 的 Filter -->
<filter>
  <description>shiro 許可權攔截</description>
  <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>
</filter-mapping>

3.配置shiro的applicationContext 新建一個recources/applicationContext-shiro.xml  (注意按自己的mvc配置命名)

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 啟用shrio授權註解攔截方式 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 裝配 securityManager -->
        <property name="securityManager" ref="securityManager"/>
        <!-- 配置登陸頁面 -->
        <property name="loginUrl" value="/login.html"/>
        <!-- 登陸成功後的一面 -->
        <property name="successUrl" value="/success.html"/>
        <property name="unauthorizedUrl" value="/403.html"/>
        <!-- 具體配置需要攔截哪些 URL, 以及訪問對應的 URL 時使用 Shiro 的什麼 Filter 進行攔截.  -->
        <property name="filterChainDefinitions">
            <value>
                /login.html = anon
                /subLogin = anon
                /roles = roles[admin]
                /testPerms = roles[admin],perms[ordertable:add]
                /** = roles["admin"]
            </value>
        </property>
    </bean>

    <!-- 配置進行授權和認證的 Realm -->
    <bean id="myRealm" class="cn.longteng.shiro.realm.ShiroDbRealm">
    </bean>

    <!-- 配置 Shiro 的 SecurityManager Bean. -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
    </bean>
</beans>

4.自定義Realm,realm時shiro驗證身份和許可權的邏輯控制器,裡面理論上是放賬號和許可權的資料庫查詢和判斷邏輯 新建一個ShiroDbRealm繼承AuthorizingRealm,會要求你重寫兩個方法 許可權授權---doGetAuthorizationInfo(PrincipalCollection principals) 賬號認證---doGetAuthenticationInfo(AuthenticationToken token)

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 java.util.HashSet;
import java.util.Set;

/**
 * @Description:
 * @Author longteng
 * @Version v2.0
 * @Date: 2018/9/6
 * @since:
 */
public class ShiroDbRealm extends AuthorizingRealm {
    //授權
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
    {
        System.out.println("資料庫進行許可權認證");
        //通過傳過來的資訊中,獲取使用者名稱
        String userName = (String) principals.getPrimaryPrincipal();

        //從資料庫獲取角色與許可權
        Set<String> setsR = new HashSet<String>();
        Set<String> setsP = new HashSet<String>();
        getRolesAndPermissionOnDB(userName,setsR,setsP);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setRoles(setsR);;
        simpleAuthorizationInfo.setStringPermissions(setsP);
        return simpleAuthorizationInfo;
    }


    //認證
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
    {
        System.out.println("資料庫進行賬號認證");
        //通過傳過來的資訊中,獲取使用者名稱
        String userName = (String) token.getPrincipal();

        //通過使用者名稱到資料庫中獲取憑證
        String password =getPassOnDB(userName);
        if (password == null)
        {
            return null;
        }

        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, password, "CustomRealm");

        return authenticationInfo;
    }

    private String getPassOnDB(final String username)
    {
        //從資料庫裡查出密碼是多少

        //………………
        return "123456";//假設查出來是123456
    }


    private void getRolesAndPermissionOnDB(String username, Set<String> setsR, Set<String> setsP)
    {
        //從資料庫裡查出該賬號的授權資料

        //…………

        //假設查出來如下
        setsR.add("admin");         //admin角色

        setsP.add("ordertable:add");//許可權
        setsP.add("ordertable:del");
    }

}

5.新增以下介個xml用作登陸驗證與提示

403.html:

{"ret":"fail","msg":"未經身份驗證"}

Success.html:

{"ret":"success","msg":"登陸成功"}

Login.html:

<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
    <table>登陸</table>
</head>
<body>

    <form action="subLogin" method="post">
        使用者名稱:<input type="text" name="username"><br>
        密碼:<input type="text" name="password" ><br>
        <input type="submit" name="登陸">
    </form>
</body>
</html>

Logout.html:

<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
    <table>是否登出:</table>
</head>
<body>

    <form action="subLogout" method="post">
        <input type="submit" name="登出">
    </form>
</body>
</html>

6.編輯subLogin介面和sunLogout介面 (1)建立類UserController編寫介面

 

import cn.longteng.web.comtroller.entity.User;
import cn.longteng.web.utils.RetMsg;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;


/**
 * @Description:
 * @Author longteng
 * @Version v2.0
 * @Date: 2018/9/6
 * @since:
 */
@Controller
public class UserController
{
    @RequestMapping(value = "/subLogin", method = RequestMethod.POST, produces="text/html; charset=UTF-8")
    @ResponseBody
    public String subLogin(User user)
    {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());

        try
        {
            token.setRememberMe(user.isRemenmberMe());

            subject.login(token);
        }
        catch (AuthenticationException e)
        {
            return RetMsg.FL(e.toString());
        }

        return RetMsg.SU("登陸成功");
    }

    @RequestMapping(value = "/subLogout", method = RequestMethod.POST, produces="text/html; charset=UTF-8")
    @ResponseBody
    public String subLogout()
    {
        Subject subject = SecurityUtils.getSubject();

        try
        {

            subject.logout();
        }
        catch (AuthenticationException e)
        {
            return RetMsg.FL(e.toString());
        }

        return RetMsg.SU("登出成功");
    }
}

(2)建立類User類用於引數接收。

public class User
{

    private String username;
    private String password;
    private boolean remenmberMe;
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public boolean isRemenmberMe() {
        return remenmberMe;
    }

    public void setRemenmberMe(boolean remenmberMe) {
        this.remenmberMe = remenmberMe;
    }
}

7.好了,我們可以開始我們的介面開發了。配合前面的配置

8.我們來測試以下成果 (1)執行起來後你會發現訪問任何介面都會跳轉到login.html

(2)只有當我們輸入賬號密碼登陸後,接口才能正常呼叫

(3)介面就可以使用了

9.題外話,如果你做前後端分離的應用,例如為Android的app提供介面,我們怎麼進行驗證呢? (1)首先推薦安裝一個postman測試工具 (2)按以下配置設定介面

(3)呼叫成功後檢視cookie

(4)配置帶該cookie的介面

10.Ok大功告成 當然了,還有MD5密碼加密加鹽,還有自定義cookie,還有記住驗證,還有驗證有效期,還有redis的快取,還有賬號比對的優化不要去查那麼多次資料。我在後面的的文章給大家介紹。該程式碼只是shiro結合mvc的原型。還有很多優化和技巧要我們去挖掘,筆者也是才寫完一個功能架構,寫出這篇文章,大家互相學習,不喜勿噴。(以上程式碼說明也許有部分遺漏或者錯誤,如有疑問請下載完整程式碼)。建議擁有springmvc基礎再學習該知識