新建web项目

  

web.xml

  修改web.xml,在里面加了个过滤器。 这个过滤器的作用,简单的说,就是 Shiro 入门里的TestShiro 这部分的工作,悄悄的干了。

//加载配置文件,并获取工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//获取安全管理者实例
SecurityManager sm = factory.getInstance();
//将安全管理者放入全局对象
SecurityUtils.setSecurityManager(sm);
 <web-app>
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<context-param>
<param-name>shiroEnvironmentClass</param-name>
<param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value><!-- 默认先从/WEB-INF/shiro.ini,如果没有找classpath:shiro.ini -->
</context-param>
<context-param>
<param-name>shiroConfigLocations</param-name>
<param-value>classpath:shiro.ini</param-value>
</context-param>
<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>

User DAO DatabaseRealm

这三个类和前面一样,不用多说。

User.java

 package com.how2java;

 public class User {

     private int id;
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
} }

点击展开

DAO

 package com.how2java;

 import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set; public class DAO {
public DAO() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8", "root",
"admin");
} public String getPassword(String userName) {
String sql = "select password from user where name = ?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) { ps.setString(1, userName); ResultSet rs = ps.executeQuery(); if (rs.next())
return rs.getString("password"); } catch (SQLException e) { e.printStackTrace();
}
return null;
} public Set<String> listRoles(String userName) { Set<String> roles = new HashSet<>();
String sql = "select r.name from user u "
+ "left join user_role ur on u.id = ur.uid "
+ "left join Role r on r.id = ur.rid "
+ "where u.name = ?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, userName);
ResultSet rs = ps.executeQuery(); while (rs.next()) {
roles.add(rs.getString(1));
} } catch (SQLException e) { e.printStackTrace();
}
return roles;
}
public Set<String> listPermissions(String userName) {
Set<String> permissions = new HashSet<>();
String sql =
"select p.name from user u "+
"left join user_role ru on u.id = ru.uid "+
"left join role r on r.id = ru.rid "+
"left join role_permission rp on r.id = rp.rid "+
"left join permission p on p.id = rp.pid "+
"where u.name =?"; try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) { ps.setString(1, userName); ResultSet rs = ps.executeQuery(); while (rs.next()) {
permissions.add(rs.getString(1));
} } catch (SQLException e) { e.printStackTrace();
}
return permissions;
}
public static void main(String[] args) {
System.out.println(new DAO().listRoles("zhang3"));
System.out.println(new DAO().listRoles("li4"));
System.out.println(new DAO().listPermissions("zhang3"));
System.out.println(new DAO().listPermissions("li4"));
}
}

点击展开

DatabaseRealm

 package com.how2java;

 import java.util.Set;

 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.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection; public class DatabaseRealm extends AuthorizingRealm { @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//能进入到这里,表示账号已经通过验证了
String userName =(String) principalCollection.getPrimaryPrincipal();
//通过DAO获取角色和权限
Set<String> permissions = new DAO().listPermissions(userName);
Set<String> roles = new DAO().listRoles(userName); //授权对象
SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
//把通过DAO获取到的角色和权限放进去
s.setStringPermissions(permissions);
s.setRoles(roles);
return s;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取账号密码
UsernamePasswordToken t = (UsernamePasswordToken) token;
String userName= token.getPrincipal().toString();
String password= new String( t.getPassword());
//获取数据库中的密码
String passwordInDB = new DAO().getPassword(userName); //如果为空就是账号不存在,如果不相同就是密码错误,但是都抛出AuthenticationException,而不是抛出具体错误原因,免得给破解者提供帮助信息
if(null==passwordInDB || !passwordInDB.equals(password))
throw new AuthenticationException(); //认证信息里存放账号密码, getName() 是当前Realm的继承方法,通常返回当前类名 :databaseRealm
SimpleAuthenticationInfo a = new SimpleAuthenticationInfo(userName,password,getName());
return a;
} }

点击展开

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

 [main]
#使用数据库进行验证和授权
databaseRealm=com.how2java.DatabaseRealm
securityManager.realms=$databaseRealm #当访问需要验证的页面,但是又没有验证的情况下,跳转到login.jsp
authc.loginUrl=/login.jsp
#当访问需要角色的页面,但是又不拥有这个角色的情况下,跳转到noroles.jsp
roles.unauthorizedUrl=/noRoles.jsp
#当访问需要权限的页面,但是又不拥有这个权限的情况下,跳转到noperms.jsp
perms.unauthorizedUrl=/noPerms.jsp #users,roles和perms都通过前面知识点的数据库配置了
[users] #urls用来指定哪些资源需要什么对应的授权才能使用
[urls]
#doLogout地址就会进行退出行为
/doLogout=logout
#login.jsp,noroles.jsp,noperms.jsp 可以匿名访问
/login.jsp=anon
/noroles.jsp=anon
/noperms.jsp=anon #查询所有产品,需要登录后才可以查看
/listProduct.jsp=authc
#增加商品不仅需要登录,而且要拥有 productManager 权限才可以操作
/deleteProduct.jsp=authc,roles[productManager]
#删除商品,不仅需要登录,而且要拥有 deleteProduct 权限才可以操作
/deleteOrder.jsp=authc,perms["deleteOrder"]

style.css

新建个样式文件,后面的 jsp 会用得着
本文件位于: WebContent/static/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的

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <link rel="stylesheet" type="text/css" href="static/css/style.css" /> </head>
<body> <div class="workingroom">
<div class="loginDiv"> <c:if test="${empty subject.principal}">
<a href="login.jsp">登录</a><br>
</c:if>
<c:if test="${!empty subject.principal}">
<span class="desc">你好,${subject.principal},</span>
<a href="doLogout">退出</a><br>
</c:if> <a href="listProduct.jsp">查看产品</a><span class="desc">(登录后才可以查看) </span><br>
<a href="deleteProduct.jsp">删除产品</a><span class="desc">(要有产品管理员角色, zhang3没有,li4 有) </span><br>
<a href="deleteOrder.jsp">删除订单</a><span class="desc">(要有删除订单权限, zhang3有,li4没有) </span><br>
</div> </body>
</html>

login.jsp

登陆页面,如果有错误返回会显示错误. 账号密码也写在下面,方便输入

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*"%> <!DOCTYPE html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="static/css/style.css" /> <div class="workingroom"> <div class="errorInfo">${error}</div>
<form action="login" method="post">
账号: <input type="text" name="name"> <br>
密码: <input type="password" name="password"> <br>
<br>
<input type="submit" value="登录">
<br>
<br>
<div>
<span class="desc">账号:zhang3 密码:12345 角色:admin</span><br>
<span class="desc">账号:li4 密码:abcde 角色:productManager</span><br>
</div> </form>
</div>

listProduct.jsp

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

/listProduct.jsp=authc 

这个页面要在登录之后才能访问

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*"%> <!DOCTYPE html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="static/css/style.css" /> <div class="workingroom"> listProduct.jsp ,能进来,就表示已经登录成功了
<br>
<a href="#" onClick="javascript:history.back()">返回</a>
</div>

deleteOrder.jsp

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

/deleteProduct.jsp=authc,roles[admin]  

这个页面要在登录之后,并且有角色的前提下才能访问。

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*"%> <!DOCTYPE html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="static/css/style.css" /> <div class="workingroom"> deleteOrder.jsp ,能进来,就表示有deleteOrder权限
<br>
<a href="#" onClick="javascript:history.back()">返回</a>
</div>

deleteProduct.jsp

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

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

这个页面要在登录之后,并且有权限的前提下才能访问。

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*"%> <!DOCTYPE html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="static/css/style.css" /> <div class="workingroom"> deleteProduct.jsp,能进来<br>就表示拥有 productManager 角色
<br>
<a href="#" onClick="javascript:history.back()">返回</a>
</div>

noRoles.jsp

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

roles.unauthorizedUrl=/noRoles.jsp

当没有角色的时候,就会跳转到这里来。

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

noPerms.jsp

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

perms.unauthorizedUrl=/noPerms.jsp

当没有权限的时候,就会跳转到这里来。

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*"%> <!DOCTYPE html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="static/css/style.css" /> <div class="workingroom"> 权限不足
<br>
<a href="#" onClick="javascript:history.back()">返回</a>
</div>

最后

启动tomcat,试试各个功能吧!并理一理其中的逻辑以及思考它的原理。

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

Apache Shiro(四)-登录认证和权限管理WEB支持(Servlet)的更多相关文章

  1. Apache Shiro(一)-登录认证和权限管理初识

    What is Apache Shiro? Apache Shiro是一个功能强大.灵活的,开源的安全框架.它可以干净利落地处理身份验证.授权.企业会话管理和加密. Apache Shiro的首要目标 ...

  2. Apache Shiro(五)-登录认证和权限管理ssm

    创建一个web动态项目 jar包 web.xml web.xml做了如下几件事情1. 指定spring的配置文件有两个 applicationContext.xml: 用于链接数据库的 applica ...

  3. Apache Shiro(三)-登录认证和权限管理MD5加密

    md5 加密 在前面的例子里,用户密码是明文的,这样是有巨大风险的,一旦泄露,就不好了.所以,通常都会采用非对称加密,什么是非对称呢?就是不可逆的,而 md5 就是这样一个算法.如代码所示 123 用 ...

  4. Apache Shiro(二)-登录认证和权限管理数据库操作

    数据库支持 在上一篇中使用ini 配置文件进行了相关权限数据的配置. 但是实际工作中,我们都会把权限相关的内容放在数据库里. 所以本知识点讲解如何放在数据库里来撸. RBAC 概念 RBAC 是当下权 ...

  5. Spring Cloud之路:(七)SpringBoot+Shiro实现登录认证和权限管理

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/sage_wang/article/details/79592269一.Shiro介绍1.Shiro是 ...

  6. spring boot(十四)shiro登录认证与权限管理

    这篇文章我们来学习如何使用Spring Boot集成Apache Shiro.安全应该是互联网公司的一道生命线,几乎任何的公司都会涉及到这方面的需求.在Java领域一般有Spring Security ...

  7. Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

    本文是接着上篇博客写的:Spring boot 入门(三):SpringBoot 集成结合 AdminLTE(Freemarker),利用 generate 自动生成代码,利用 DataTable 和 ...

  8. shiro实现APP、web统一登录认证和权限管理

    先说下背景,项目包含一个管理系统(web)和门户网站(web),还有一个手机APP(包括Android和IOS),三个系统共用一个后端,在后端使用shiro进行登录认证和权限控制.好的,那么问题来了w ...

  9. Springboot-shiro-redis实现登录认证和权限管理

    Springboot-shiro-redis实现登录认证和权限管理 在学习之前: 首先进行一下Apache Shiro和Shiro比较: Apache Shiro是一个功能强大.灵活的,开源的安全框架 ...

随机推荐

  1. 12-scanf("%*s")与printf("%*s")

    在scanf里用*修饰符,是起到过滤读入的作用.比如一个有三列数值的数据,我只想得到第2列数值,可以在循环里用scanf(“%*d%d%*d”,a[i])来读入第i行的第2个数值到a[i].     ...

  2. ssh -X前设置DISPLAY=localhost:0

    如果是在windows上用XMing做XServer,前面的localhost不能省,否则会被当作一个unix domain socket,而XMing没有实现这个功能,所以会出错 connect / ...

  3. GridView删除行

    在GridView绑定数据的时候需要设置该GridView的主键值,设置的这个主键与取出来的数据的一个字段对应.比如,取出来的数据表中有个ID的字段,那设这个ID为该GridView的主键是比较好的. ...

  4. Linux下的fdlisk - l 用法解析-入门篇

    fdlisk - l 的含义是查看linux下面的磁盘分区大小.这个大小包含了很多信息. 我们来看度娘的一则介绍: FDISK进行硬盘分区从实质上说就是对硬盘的一种格式化.当我们创建分区时,就已经设置 ...

  5. (转)MongoDB入门分享-笔记整理精选

    原文地址:http://www.cnblogs.com/Kummy/p/3372729.html 最近在学习MongoDB,怕以后忘记,自己做了一个整理,给不知道的小伙伴一起分享学习一下. 第一步&g ...

  6. 图的遍历——DFS

    原创 图的遍历有DFS和BFS两种,现选用DFS遍历图. 存储图用邻接矩阵,图有v个顶点,e条边,邻接矩阵就是一个VxV的矩阵: 若顶点1和顶点5之间有连线,则矩阵元素[1,5]置1,若是无向图[5, ...

  7. Django实战之古风博客

    感谢 感谢杨青 大大的古风模板,设计的很棒,给个赞. 如有侵权,请联系我 运行环境 python3.6 Django==1.11.4 django-ckeditor==5.4.0 django-js- ...

  8. 【转】微信中MMAlert(半透明底部弹出菜单)的使用介绍

    原文地址:http://blog.csdn.net/singwhatiwanna/article/details/8892930 果大家时常用过微信或者用过iphone,就会发现有种从底部弹出的半透明 ...

  9. Task ContinueWith

    前正无生意,且记Task.ContinueWith之用法. using System; using System.Collections.Generic; using System.Diagnosti ...

  10. 使用Linq读取资源文件

    ResXResourceReader resxr = new ResXResourceReader(txt_WebResourceOpenFile.Text); IEnumerable<Dict ...