Apache Shiro(四)-登錄認證和權限管理WEB支持(Servlet)


新建web項目

  

web.xml

  修改web.xml,在里面加了個過濾器。 這個過濾器的作用,簡單的說,就是 Shiro 入門里的TestShiro 這部分的工作,悄悄的干了。

//加載配置文件,並獲取工廠
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//獲取安全管理者實例
SecurityManager sm = factory.getInstance();
//將安全管理者放入全局對象
SecurityUtils.setSecurityManager(sm);

 

 1 <web-app>
 2     <listener>
 3         <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
 4     </listener>
 5     <context-param>
 6         <param-name>shiroEnvironmentClass</param-name>
 7         <param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value><!-- 默認先從/WEB-INF/shiro.ini,如果沒有找classpath:shiro.ini -->
 8     </context-param>
 9     <context-param>
10         <param-name>shiroConfigLocations</param-name>
11         <param-value>classpath:shiro.ini</param-value>
12     </context-param>
13     <filter>
14         <filter-name>shiroFilter</filter-name>
15         <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
16     </filter>
17     <filter-mapping>
18         <filter-name>shiroFilter</filter-name>
19         <url-pattern>/*</url-pattern>
20     </filter-mapping>
21 </web-app>

User DAO DatabaseRealm

這三個類和前面一樣,不用多說。

User.java

 1 package com.how2java;
 2  
 3 public class User {
 4  
 5     private int id;
 6     private String name;
 7     private String password;
 8     public String getName() {
 9         return name;
10     }
11     public void setName(String name) {
12         this.name = name;
13     }
14     public String getPassword() {
15         return password;
16     }
17     public void setPassword(String password) {
18         this.password = password;
19     }
20     public int getId() {
21         return id;
22     }
23     public void setId(int id) {
24         this.id = id;
25     }
26      
27 }
點擊展開

DAO

 1 package com.how2java;
 2  
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 import java.sql.PreparedStatement;
 6 import java.sql.ResultSet;
 7 import java.sql.SQLException;
 8 import java.util.HashSet;
 9 import java.util.Set;
10  
11 public class DAO {
12     public DAO() {
13         try {
14             Class.forName("com.mysql.jdbc.Driver");
15         } catch (ClassNotFoundException e) {
16             e.printStackTrace();
17         }
18     }
19  
20     public Connection getConnection() throws SQLException {
21         return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8", "root",
22                 "admin");
23     }
24  
25     public String getPassword(String userName) {
26         String sql = "select password from user where name = ?";
27         try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
28              
29             ps.setString(1, userName);
30              
31             ResultSet rs = ps.executeQuery();
32  
33             if (rs.next())
34                 return rs.getString("password");
35  
36         } catch (SQLException e) {
37  
38             e.printStackTrace();
39         }
40         return null;
41     }
42      
43     public Set<String> listRoles(String userName) {
44          
45         Set<String> roles = new HashSet<>();
46         String sql = "select r.name from user u "
47                 + "left join user_role ur on u.id = ur.uid "
48                 + "left join Role r on r.id = ur.rid "
49                 + "where u.name = ?";
50         try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
51             ps.setString(1, userName);
52             ResultSet rs = ps.executeQuery();
53              
54             while (rs.next()) {
55                 roles.add(rs.getString(1));
56             }
57              
58         } catch (SQLException e) {
59              
60             e.printStackTrace();
61         }
62         return roles;
63     }
64     public Set<String> listPermissions(String userName) {
65         Set<String> permissions = new HashSet<>();
66         String sql =
67             "select p.name from user u "+
68             "left join user_role ru on u.id = ru.uid "+
69             "left join role r on r.id = ru.rid "+
70             "left join role_permission rp on r.id = rp.rid "+
71             "left join permission p on p.id = rp.pid "+
72             "where u.name =?";
73          
74         try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
75              
76             ps.setString(1, userName);
77              
78             ResultSet rs = ps.executeQuery();
79              
80             while (rs.next()) {
81                 permissions.add(rs.getString(1));
82             }
83              
84         } catch (SQLException e) {
85              
86             e.printStackTrace();
87         }
88         return permissions;
89     }
90     public static void main(String[] args) {
91         System.out.println(new DAO().listRoles("zhang3"));
92         System.out.println(new DAO().listRoles("li4"));
93         System.out.println(new DAO().listPermissions("zhang3"));
94         System.out.println(new DAO().listPermissions("li4"));
95     }
96 }
點擊展開

DatabaseRealm

 1 package com.how2java;
 2  
 3 import java.util.Set;
 4  
 5 import org.apache.shiro.authc.AuthenticationException;
 6 import org.apache.shiro.authc.AuthenticationInfo;
 7 import org.apache.shiro.authc.AuthenticationToken;
 8 import org.apache.shiro.authc.SimpleAuthenticationInfo;
 9 import org.apache.shiro.authc.UsernamePasswordToken;
10 import org.apache.shiro.authz.AuthorizationInfo;
11 import org.apache.shiro.authz.SimpleAuthorizationInfo;
12 import org.apache.shiro.realm.AuthorizingRealm;
13 import org.apache.shiro.subject.PrincipalCollection;
14  
15 public class DatabaseRealm extends AuthorizingRealm {
16  
17     @Override
18     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
19         //能進入到這里,表示賬號已經通過驗證了
20         String userName =(String) principalCollection.getPrimaryPrincipal();
21         //通過DAO獲取角色和權限
22         Set<String> permissions = new DAO().listPermissions(userName);
23         Set<String> roles = new DAO().listRoles(userName);
24          
25         //授權對象
26         SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
27         //把通過DAO獲取到的角色和權限放進去
28         s.setStringPermissions(permissions);
29         s.setRoles(roles);
30         return s;
31     }
32  
33     @Override
34     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
35         //獲取賬號密碼
36         UsernamePasswordToken t = (UsernamePasswordToken) token;
37         String userName= token.getPrincipal().toString();
38         String password= new String( t.getPassword());
39         //獲取數據庫中的密碼
40         String passwordInDB = new DAO().getPassword(userName);
41  
42         //如果為空就是賬號不存在,如果不相同就是密碼錯誤,但是都拋出AuthenticationException,而不是拋出具體錯誤原因,免得給破解者提供幫助信息
43         if(null==passwordInDB || !passwordInDB.equals(password))
44             throw new AuthenticationException();
45          
46         //認證信息里存放賬號密碼, getName() 是當前Realm的繼承方法,通常返回當前類名 :databaseRealm
47         SimpleAuthenticationInfo a = new SimpleAuthenticationInfo(userName,password,getName());
48         return a;
49     }
50  
51 }
點擊展開

LoginServlet

LoginServlet 映射路徑/login的訪問。
獲取賬號和密碼,然后組成UsernamePasswordToken 對象,仍給Shiro進行判斷。 如果判斷不報錯,即表示成功,客戶端跳轉到根目錄,否則返回login.jsp,並帶上錯誤信息
登錄成功后還會把subject放在shiro的session對象里,shiro的這個session和httpsession是串通好了的,所以在這里放了,它會自動放在httpsession里,它們之間是同步的。

package com.how2java;
 
import java.io.IOException;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
 
@WebServlet(name = "loginServlet", urlPatterns = "/login") 
public class LoginServlet extends HttpServlet { 
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
      throws ServletException, IOException { 
        String name = req.getParameter("name"); 
        String password = req.getParameter("password"); 
        Subject subject = SecurityUtils.getSubject(); 
        UsernamePasswordToken token = new UsernamePasswordToken(name, password); 
        try { 
            subject.login(token);
            Session session=subject.getSession();
            session.setAttribute("subject", subject);
             
            resp.sendRedirect("");
        } catch (AuthenticationException e) { 
            req.setAttribute("error", "驗證失敗"); 
            req.getRequestDispatcher("login.jsp").forward(req, resp);
        } 
    } 
} 

shiro.ini

 1 [main]  
 2 #使用數據庫進行驗證和授權
 3 databaseRealm=com.how2java.DatabaseRealm
 4 securityManager.realms=$databaseRealm
 5 
 6 #當訪問需要驗證的頁面,但是又沒有驗證的情況下,跳轉到login.jsp
 7 authc.loginUrl=/login.jsp
 8 #當訪問需要角色的頁面,但是又不擁有這個角色的情況下,跳轉到noroles.jsp
 9 roles.unauthorizedUrl=/noRoles.jsp
10 #當訪問需要權限的頁面,但是又不擁有這個權限的情況下,跳轉到noperms.jsp
11 perms.unauthorizedUrl=/noPerms.jsp
12 
13 #users,roles和perms都通過前面知識點的數據庫配置了
14 [users]  
15 
16 #urls用來指定哪些資源需要什么對應的授權才能使用
17 [urls]  
18 #doLogout地址就會進行退出行為
19 /doLogout=logout
20 #login.jsp,noroles.jsp,noperms.jsp 可以匿名訪問
21 /login.jsp=anon
22 /noroles.jsp=anon
23 /noperms.jsp=anon
24 
25 #查詢所有產品,需要登錄后才可以查看
26 /listProduct.jsp=authc  
27 #增加商品不僅需要登錄,而且要擁有 productManager 權限才可以操作
28 /deleteProduct.jsp=authc,roles[productManager]  
29 #刪除商品,不僅需要登錄,而且要擁有 deleteProduct 權限才可以操作
30 /deleteOrder.jsp=authc,perms["deleteOrder"]   

style.css

新建個樣式文件,后面的 jsp 會用得着
本文件位於: WebContent/static/css/style.css 下
style.css
 
span.desc{
    margin-left:20px;
    color:gray;
}
div.workingroom{
    margin:200px auto;
    width:400px;
}
div.workingroom a{
    display:inline-block;
    margin-top:20px;
}
div.loginDiv{
    text-align: left;
}
div.errorInfo{
    color:red;
    font-size:0.65em;
}

 

index.jsp

1. 通過 ${subject.principal} 來判斷用戶是否登錄,如果登錄過了就顯示退出,如果未登錄就顯示登錄按鈕
2. 提供3個超鏈,分別要 登錄后才可以查看,有角色才能看,有權限才能看,便於進行測試
注: subject 是在 LoginServlet 里放進session的
index.jsp
 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <html>
 4 <head>
 5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 6  
 7 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
 8  
 9 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
10  
11 </head>
12 <body>
13  
14 <div class="workingroom">
15     <div class="loginDiv">
16      
17     <c:if test="${empty subject.principal}">
18         <a href="login.jsp">登錄</a><br>
19     </c:if>
20     <c:if test="${!empty subject.principal}">
21         <span class="desc">你好,${subject.principal},</span>
22         <a href="doLogout">退出</a><br>
23     </c:if>
24          
25     <a href="listProduct.jsp">查看產品</a><span class="desc">(登錄后才可以查看) </span><br>
26     <a href="deleteProduct.jsp">刪除產品</a><span  class="desc">(要有產品管理員角色, zhang3沒有,li4 有) </span><br>
27     <a href="deleteOrder.jsp">刪除訂單</a><span class="desc">(要有刪除訂單權限, zhang3有,li4沒有) </span><br>
28 </div>
29  
30 </body>
31 </html>

login.jsp

登陸頁面,如果有錯誤返回會顯示錯誤. 賬號密碼也寫在下面,方便輸入
login.jsp
 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8" import="java.util.*"%>
 3   
 4 <!DOCTYPE html>
 5   
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
 8  
 9 <div class="workingroom">
10  
11 <div class="errorInfo">${error}</div>
12     <form action="login" method="post">
13         賬號: <input type="text" name="name"> <br>
14         密碼: <input type="password" name="password"> <br>
15         <br>
16         <input type="submit" value="登錄">
17         <br>
18         <br>
19     <div>
20         <span class="desc">賬號:zhang3 密碼:12345 角色:admin</span><br>
21         <span class="desc">賬號:li4 密碼:abcde 角色:productManager</span><br>
22     </div>
23          
24     </form>
25 </div>

listProduct.jsp

根據 shiro.ini 里的配置信息:

/listProduct.jsp=authc 

這個頁面要在登錄之后才能訪問

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8" import="java.util.*"%>
 3   
 4 <!DOCTYPE html>
 5   
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
 8  
 9 <div class="workingroom">
10  
11     listProduct.jsp ,能進來,就表示已經登錄成功了
12     <br>
13     <a href="#" onClick="javascript:history.back()">返回</a>
14 </div>

deleteOrder.jsp

根據 shiro.ini 里的配置信息:

/deleteProduct.jsp=authc,roles[admin]  

這個頁面要在登錄之后,並且有角色的前提下才能訪問。

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8" import="java.util.*"%>
 3   
 4 <!DOCTYPE html>
 5   
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
 8  
 9 <div class="workingroom">
10  
11     deleteOrder.jsp ,能進來,就表示有deleteOrder權限
12     <br>
13     <a href="#" onClick="javascript:history.back()">返回</a>
14 </div>

deleteProduct.jsp

根據 shiro.ini 里的配置信息:

/deleteOrder.jsp=authc,perms["deleteOrder"]  

這個頁面要在登錄之后,並且有權限的前提下才能訪問。

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8" import="java.util.*"%>
 3   
 4 <!DOCTYPE html>
 5   
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
 8  
 9 <div class="workingroom">
10  
11     deleteProduct.jsp,能進來<br>就表示擁有 productManager 角色
12     <br>
13     <a href="#" onClick="javascript:history.back()">返回</a>
14 </div>

noRoles.jsp

根據 shiro.ini 里的配置信息:

roles.unauthorizedUrl=/noRoles.jsp

當沒有角色的時候,就會跳轉到這里來。

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8" import="java.util.*"%>
 3   
 4 <!DOCTYPE html>
 5   
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
 8  
 9 <div class="workingroom">
10  
11     角色不匹配
12     <br>
13     <a href="#" onClick="javascript:history.back()">返回</a>
14 </div>

noPerms.jsp

根據 shiro.ini 里的配置信息:

perms.unauthorizedUrl=/noPerms.jsp

當沒有權限的時候,就會跳轉到這里來。

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8" import="java.util.*"%>
 3   
 4 <!DOCTYPE html>
 5   
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
 8  
 9 <div class="workingroom">
10  
11     權限不足
12     <br>
13     <a href="#" onClick="javascript:history.back()">返回</a>
14 </div>

 

最后

啟動tomcat,試試各個功能吧!並理一理其中的邏輯以及思考它的原理。

代碼地址:https://gitee.com/fengyuduke/my_open_resources/blob/master/shiro-web.zip

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM