Shiro整合SpringMVC在Web环境下实现登录认证
注:shiro的JavaEE环境搭建可以参考Shiro学习笔记(五):https://blog.youkuaiyun.com/weixin_41178230/article/details/82873443
Web环境下实现认证的基本流程:
1、jsp页面:包含用户信息,并封装到form表单中;
2、Spring MVC控制器:处理用户请求:
- 获取用户的登录信息
- shiro API来完成用户认证
1)获取Subject类型的实例
2)判断用户是否已经登录
3)使用UsernamePasswordToken对象来封装用户名和密码
4)使用Subject中的login(token)方法
5)Realm:从数据库中获取安全数据
- 将token的类型转换为UsernamePasswordToken
- 从转换好的对象中获取用户名即可
- 查询数据库,从数据库中查询是否存在相对应的用户名和密码
- 如果查询到了,就封装结果,返回给我们的调用
- 如果没有查询到,就抛出一个异常
springmvc-config.xml:
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置包扫描器,扫描@Controller注解的类 -->
<context:component-scan base-package="com.shiro.controller" />
<!-- 加载注解驱动 -->
<mvc:annotation-driven />
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 开启aop,对类代理 -->
<aop:config proxy-target-class="true"></aop:config>
</beans>
login.jsp(包含用户信息,并封装到form表单中):
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>
<form action="login.action" method="post">
username:<input type="text" name="username" />
<br />
password:<input type="password" name="password" />
<br />
<input type="submit" value="登录" />
</form>
</body>
</html>
配置applicationContext.xml中的请求拦截路径:
LoginController.java:
package com.shiro.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class LoginController {
@RequestMapping("/login.action")
public String Login(@RequestParam("username") String username,@RequestParam("password") String password) {
//获取Subject类型的实例
System.out.println(username+"--------"+password);
Subject currentUser = SecurityUtils.getSubject();
//判断用户是否已经登录
if(currentUser.isAuthenticated()==false) {
//使用UsernamePasswordToken对象来封装用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//使用Subject中的login(token)方法
try{
currentUser.login(token);
System.out.println("登录验证");
}catch(AuthenticationException e) {
return "error";
}
}
return "index";
}
}
index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>
<p>Index Page.</p>
<a href="logout">LOGOUT</a><!--点击按钮注销用户并退出 -->
</body>
</html>
error.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>
<h3>Error Page.</h3>
</body>
</html>
ShiroRealm.java
package com.shiro.realm;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
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.realm.AuthenticatingRealm;
//Realm:从数据库中获取并处理安全数据
public class ShiroRealm extends AuthenticatingRealm {
/*
* 可以使用AuthenticationInfo接口中的SimpleAuthenticationInfo实现类来封装正确的用户名和密码
*
* doGetAuthenticationInfo方法:获取认证信息,如果数据库中没有数据,就返回null;反之,则返回指定的类型的对象
*
* AuthenticationToken token:就是我们要认证的token
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
/*
* 将token的类型转换为UsernamePasswordToken
* 从转换好的对象中获取用户名即可
* 查询数据库,从数据库中查询是否存在相对应的用户名和密码
* 如果查询到了,就封装结果,返回给我们的调用
* 如果没有查询到,就抛出一个异常
*/
SimpleAuthenticationInfo info = null;
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String userName = upToken.getUsername();
//使用jdbc原生的API来从数据库中查询数据
try {
//获取驱动
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/test";
String username = "root";
String password = "123";
Connection con = DriverManager.getConnection(url, username, password);
PreparedStatement ps = con.prepareStatement("select * from sec_user where user_name=?");
ps.setString(1, userName);
ResultSet rs = ps.executeQuery();
if(rs.next()) {
Object principal = userName;
Object credentials = rs.getString(3);
String realmName = this.getName();
info = new SimpleAuthenticationInfo(principal, credentials, realmName);
}else {
throw new AuthenticationException();
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return info;
}
}
创建数据表并插入数据:
CREATE TABLE `sec_user` (
`user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_name` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`password` varchar(128) COLLATE utf8_bin DEFAULT NULL,
`created_time` datetime DEFAULT NULL,
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
insert into sec_user(user_name,password) values ('tom','123456');
insert into sec_user(user_name,password) values ('jim','123456');