• 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

  • 相关阅读:
    file & iconv
    UML类图思考
    Rust PhantomData and dropck rgb
    golang recover rgb
    帮上小学的女儿写的一篇文章春夏秋冬
    SAP B1在添加物料主数据时,出现错误提示‘xxxx代码已存在’的解决方法
    SAP B1外协物料处理方法
    SAP B1外发加工件成本的处理方法(曹玉平于奥莱照明)
    SAP B1存在的BUG
    交叉表的实殃及向SQL SERVER数据库中插入数据时,出现乱码或???(问号)的解决方法。
  • 原文地址:https://www.cnblogs.com/fengyuduke/p/10417103.html
Copyright © 2020-2023  润新知