1、身份验证(登录)--使用ini配置文件
1.web.xm配置,并配置2个servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>shiro-base</display-name>
<!-- 添加shiro支持,怎么配置可以参考官方文档 -->
<!--从Shiro 1.2开始引入了Environment/WebEnvironment的概念,
即由它们的实现提供相应的SecurityManager及其相应的依赖。ShiroFilter会自动找到Environment然后获取相应的依赖。
通过EnvironmentLoaderListener来创建相应的WebEnvironment,并自动绑定到ServletContext,默认使用IniWebEnvironment实现。-->
<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>
<servlet>
<servlet-name>loginServlet</servlet-name>
<servlet-class>com.changwen.shiro.shiro_web.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>loginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>adminServlet</servlet-name>
<servlet-class>com.changwen.shiro.shiro_web.servlet.AdminServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>adminServlet</servlet-name>
<url-pattern>/admin</url-pattern>
</servlet-mapping>
</web-app>
2.shiro.ini配置
ini配置部分和之前的相比将多出对url部分的配置。
[main]
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized.jsp
perms.unauthorizedUrl=/unauthorized.jsp
[users]
tom=123,admin
jack=1234,teacher
marry=1235
[roles]
admin=user:*
teacher=student:*
[urls]
/login=anon
/admin=authc
/student=roles[teacher]
/teacher=perms["user:create"]
其中最重要的就是[urls]部分的配置,其格式是: “url=拦截器[参数],拦截器[参数]”;即如果当前请求的url匹配[urls]部分的某个url模式,将会执行其配置的拦截器。比如anon拦截器表示匿名访问(即不需要登录即可访问);authc拦截器表示需要身份认证通过后才能访问;roles[admin]拦截器表示需要有admin角色授权才能访问;而perms["user:create"]拦截器表示需要有“user:create”权限才能访问。
---------------------------------------------------补充知识----------------------------------------------
url模式使用Ant风格模式
Ant路径通配符支持?、*、**,注意通配符匹配不包括目录分隔符“/”:
?:匹配一个字符,如”/admin?”将匹配/admin1,但不匹配/admin或/admin2;
*:匹配零个或多个字符串,如/admin*将匹配/admin、/admin123,但不匹配/admin/1;
**:匹配路径中的零个或多个路径,如/admin/**将匹配/admin/a或/admin/a/b。
url模式匹配顺序
url模式匹配顺序是按照在配置中的声明顺序匹配,即从头开始使用第一个匹配的url模式对应的拦截器链。如:
/bb/**=filter1
/bb/aa=filter2
/**=filter3
如果请求的url是“/bb/aa”,因为按照声明顺序进行匹配,那么将使用filter1进行拦截。拦截器将在下一节详细介绍。
---------------------------------------------------------------------------------------------------------
3.servlet代码编写
public class AdminServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("admin do get");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("admin do post");
}
}
public class LoginServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("login doget");
// 没有登陆的转发到login.jap
req.getRequestDispatcher("login.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("login dopost");
String userName=req.getParameter("userName");
String password=req.getParameter("password");
Subject subject=SecurityUtils.getSubject();
<pre name="code" class="java">//UsernamePasswordToken token=new UsernamePasswordToken(userName, CryptographyUtil.md5(password,"test"));
UsernamePasswordToken token=new UsernamePasswordToken(userName, password);try{subject.login(token);Session session=subject.getSession();System.out.println("sessionId:"+session.getId());System.out.println("sessionHost:"+session.getHost());System.out.println("sessionTimeout:"+session.getTimeout());session.setAttribute("info", "session的数据");resp.sendRedirect("success.jsp");}catch(Exception e){e.printStackTrace();req.setAttribute("errorInfo", "用户名或者密码错误");req.getRequestDispatcher("login.jsp").forward(req, resp);}}}
4.jsp
login.jsp核心代码
<form action="login" method="post">
userName:<input type="text" name="userName"/><br/>
password:<input type="password" name="password"/><br/>
<input type="submit" value="登录"/>${errorInfo}
</form>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- shiro jsp标签-->
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
欢迎你!
<!--hasRole标签:如果当前Subject有任意一个角色(或的关系)将显示body体内容。 -->
<shiro:hasRole name="admin">
用户[<shiro:principal/>]拥有角色admin<br/>
</shiro:hasRole>
</body>
</html>
2、使用数据库配置
1、定义实体及关系
即用户-角色之间是多对多关系,角色-权限之间是多对多关系;且用户和权限之间通过角色建立关系;在系统中验证时通过权限验证,角色只是权限集合,即所谓的显示角色;其实权限应该对应到资源(如菜单、URL、页面按钮、Java方法等)中,即应该将权限字符串存储到资源实体中,但是目前为了简单化,直接提取一个权限表,
// 角色表
DROP TABLE IF EXISTS t_role ;
CREATE TABLE t_role (
id INT(11) NOT NULL AUTO_INCREMENT,
roleName VARCHAR(20) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO t_role(id,roleName) VALUES (1,'admin'),(2,'teacher');
// 用户表
DROP TABLE IF EXISTS t_user;
CREATE TABLE t_user (
id INT(11) NOT NULL AUTO_INCREMENT,
userName VARCHAR(20) DEFAULT NULL,
PASSWORD VARCHAR(100) DEFAULT NULL,
roleId INT(11) DEFAULT NULL,
PRIMARY KEY (id),
KEY roleId (roleId),
CONSTRAINT t_user_ibfk_1 FOREIGN KEY (roleId) REFERENCES t_role (id)
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO t_user(id,userName,PASSWORD,roleId)
VALUES (1,'tom','123',1),(2,'jack','1234',2),(3,'marry','1235',NULL);
// 权限表
CREATE TABLE t_permission (
id INT(11) NOT NULL AUTO_INCREMENT,
permissionName VARCHAR(50) DEFAULT NULL,
roleId INT(11) DEFAULT NULL,
PRIMARY KEY (id),
KEY roleId (roleId),
CONSTRAINT t_permission_ibfk_1 FOREIGN KEY (roleId) REFERENCES t_role (id)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO t_permission(id,permissionName,roleId) VALUES (1,'user:*',1),(2,'student:*',2);
2.自定义myReaml
// 自定义的reaml
public class MyRealm extends AuthorizingRealm{
private UserDao userDao=new UserDao();
private DbUtil dbUtil=new DbUtil();
/**
* 先验证当前登录的用户
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取当前用户名
String userName=(String)token.getPrincipal();
Connection con=null;
try{
con=dbUtil.getCon();
User user=userDao.getByUserName(con, userName);
if(user!=null){
AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),"xx");
return authcInfo;
}else{
return null;
}
}catch(Exception e){
e.printStackTrace();
}finally{
try {
dbUtil.closeCon(con);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 为当前登录的用户授予角色和权限
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName=(String)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
Connection con=null;
try{
con=dbUtil.getCon();
authorizationInfo.setRoles(userDao.getRoles(con,userName));
authorizationInfo.setStringPermissions(userDao.getPermissions(con,userName));
}catch(Exception e){
e.printStackTrace();
}finally{
try {
dbUtil.closeCon(con);
} catch (Exception e) {
e.printStackTrace();
}
}
return authorizationInfo;
}
}
Userdao
public class UserDao {
public User getByUserName(Connection con,String userName)throws Exception{
User resultUser=null;
String sql="select * from t_user where userName=?";
PreparedStatement pstmt=con.prepareStatement(sql);
pstmt.setString(1, userName);
ResultSet rs=pstmt.executeQuery();
if(rs.next()){
resultUser=new User();
resultUser.setId(rs.getInt("id"));
resultUser.setUserName(rs.getString("userName"));
resultUser.setPassword(rs.getString("password"));
}
return resultUser;
}
public Set<String> getRoles(Connection con, String userName) throws Exception{
Set<String> roles=new HashSet<String>();
String sql="select * from t_user u,t_role r where u.roleId=r.id and u.userName=?";
PreparedStatement pstmt=con.prepareStatement(sql);
pstmt.setString(1, userName);
ResultSet rs=pstmt.executeQuery();
while(rs.next()){
roles.add(rs.getString("roleName"));
}
return roles;
}
public Set<String> getPermissions(Connection con, String userName)throws Exception {
Set<String> permissions=new HashSet<String>();
String sql="select * from t_user u,t_role r,t_permission p where u.roleId=r.id and p.roleId=r.id and u.userName=?";
PreparedStatement pstmt=con.prepareStatement(sql);
pstmt.setString(1, userName);
ResultSet rs=pstmt.executeQuery();
while(rs.next()){
permissions.add(rs.getString("permissionName"));
}
return permissions;
}
}
实例类User
public class User {
private Integer id;
private String userName;
private String password;
//省去get,set方法
}
shiro.ini配置文件
[main]
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized.jsp
perms.unauthorizedUrl=/unauthorized.jsp
#[users]
#tom=123,admin
#jack=1234,teacher
#marry=1235
#[roles]
#admin=user:*
#teacher=student:*
# 使用数据库测试替换上面
myRealm=com.changwen.shiro.shiro_web.realm.MyRealm
securityManager.realms=$myRealm
[urls]
/login=anon
/admin=authc
/student=roles[teacher]
/teacher=perms["user:create"]
--------------------------------------------------------------------------------------------
两个工具类
/**
* 数据库工具类
*/
public class DbUtil {
/**
* 获取数据库连接
*/
public Connection getCon() throws Exception{
Class.forName("com.mysql.jdbc.Driver");
Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/db_shiro", "root", "123456");
// Class.forName("oracle.jdbc.OracleDriver");
// Connection con=DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "root", "123456");
return con;
}
/**
* 关闭数据库连接
*/
public void closeCon(Connection con)throws Exception{
if(con!=null){
con.close();
}
}
// 测试
public static void main(String[] args) {
DbUtil dbUtil=new DbUtil();
try {
dbUtil.getCon();
System.out.println("数据库连接成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("数据库连接失败");
}
}
}
public class CryptographyUtil {
/**
* base64加密
*/
public static String encBase64(String str){
return Base64.encodeToString(str.getBytes());
}
/**
* base64解密
*/
public static String decBase64(String str){
return Base64.decodeToString(str);
}
/**
* Md5加密,不能解密
* @param salt 盐值
*/
public static String md5(String str,String salt){
return new Md5Hash(str,salt).toString();
}
public static void main(String[] args) {
String password="123456";
System.out.println("Base64加密:"+CryptographyUtil.encBase64(password));
System.out.println("Base64解密:"+CryptographyUtil.decBase64(CryptographyUtil.encBase64(password)));
System.out.println("Md5加密:"+CryptographyUtil.md5(password, "123456"));
}
}