认证大全(想学习其他认证,请到此链接查看)
参考文章: https://blog.csdn.net/qq_35642036/article/details/82788588
注意: 登录成功时的响应报文头会多了个Set-Cookie字段,目的是下次新的请求都会携带这些信息在请求头cookie里面
小知识点: 利用请求头中cookie中的用户身份标识来进行认证校验
官方文章(Cookie): https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies
官方文章(Set-Cookie): https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie
application.yml
server:
port: 8080
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
cache-enabled: true
mapper-locations: classpath:mapper/*Mapper.xml
global-config:
db-config:
id-type: assign_uuid
logic-delete-value: 1
logic-not-delete-value: 0
logic-delete-field: is_del
where-strategy: not_empty
update-strategy: not_empty
insertStrategy: not_empty
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
username: root
password: root
url: jdbc:p6spy:mysql://localhost:3306/lrc_blog?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
freemarker:
suffix: .html
CookieAuthInterceptor.java
package work.linruchang.qq.mybaitsplusjoin.config.interceptor;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.stream.Stream;
/**
* 作用:
*
* @author LinRuChang
* @version 1.0
* @date 2022/08/04
* @since 1.8
**/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CookieAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean authStatusFlag = false;
Cookie[] cookies = ObjectUtil.defaultIfNull(request.getCookies(), new Cookie[0]);
String cookieCredentials = Stream.of(cookies)
.filter(cookie -> StrUtil.equals(cookie.getName(), "credentials"))
.findFirst()
.map(Cookie::getValue)
.orElse(null);
String cookieUserName = Stream.of(cookies)
.filter(cookie -> StrUtil.equals(cookie.getName(), "userName"))
.findFirst()
.map(Cookie::getValue)
.orElse(null);
if(StrUtil.isAllNotBlank(cookieCredentials,cookieUserName)) {
//根据userName获取数据库存储的用户信息 == 这里我默认只取root用户
Dict dbUserInfo = getUserInfo(cookieUserName);
String dbCredentials = dbUserInfo != null ? SecureUtil.md5(StrUtil.format("{}:{}", dbUserInfo.getStr("userName"), dbUserInfo.getStr("password"))) : null;
if(StrUtil.equals(dbCredentials,cookieCredentials)) {
authStatusFlag = true;
}
}
//认证失败 == 重定向到登录页面
if (!authStatusFlag) {
StringBuffer url = request.getRequestURL();
if (request.getQueryString() != null) {
url.append("?");
url.append(request.getQueryString());
}
response.sendRedirect(StrUtil.format("/user/login?targetUrl={}?{}",request.getRequestURL().toString(),StrUtil.nullToEmpty(request.getQueryString())));
}
return authStatusFlag;
}
/**
* 自行改成获取数据库的逻辑
* @param userName
* @return
*/
public Dict getUserInfo(String userName) {
if(StrUtil.equals(userName,"admin")) {
return Dict.create()
.set("userName","admin")
.set("password", "admin123");
}
return null;
}
}
MyConfig.java
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Autowired
CookieAuthInterceptor cookieAuthInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(cookieAuthInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/static/**","/user/login/**","/user/logout/**");
}
}
UserController .java
package work.linruchang.qq.mybaitsplusjoin.controller;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import lombok.SneakyThrows;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import work.linruchang.qq.mybaitsplusjoin.common.response.CommonHttpResult;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 作用:
*
* @author LinRuChang
* @version 1.0
* @date 2022/08/05
* @since 1.8
**/
@Controller
@RequestMapping("user")
public class UserController {
/**
* 登录页面
* @param httpServletRequest
* @param modelAndView
* @return
*/
@GetMapping("login")
public ModelAndView loginPage(HttpServletRequest httpServletRequest, ModelAndView modelAndView) {
modelAndView.setViewName("login");
modelAndView.addObject("targetUrl", httpServletRequest.getParameter("targetUrl"));
return modelAndView;
}
/**
* 登录
* @param httpServletRequest
* @param httpServletResponse
* @param modelAndView
*/
@PostMapping( "login")
@SneakyThrows
public void login(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, ModelAndView modelAndView) {
String userName = httpServletRequest.getParameter("userName");
String password = httpServletRequest.getParameter("password");
String targetUrl = httpServletRequest.getParameter("targetUrl");
if (StrUtil.equals(userName, "admin") && StrUtil.equals(password, "admin123")) {
//cookie有效期为1天
Integer secondDate = Integer.valueOf(String.valueOf(DateUnit.DAY.getMillis()/1000));
String credentials = SecureUtil.md5(StrUtil.format("{}:{}", userName, password));
Cookie cookie = new Cookie("credentials", credentials);
cookie.setPath("/");
cookie.setMaxAge(secondDate);
httpServletResponse.addCookie(cookie);
//用户名-用于前端JS显示用户信息
Cookie cookie2 = new Cookie("userName", "admin");
cookie2.setPath("/");
cookie2.setMaxAge(secondDate);
httpServletResponse.addCookie(cookie2);
if (StrUtil.isNotBlank(targetUrl)) {
httpServletResponse.sendRedirect(targetUrl);
} else {
httpServletResponse.setHeader("Content-Type", "application/json;charset=UTF-8");
httpServletResponse.getWriter().print(JSONUtil.toJsonStr(CommonHttpResult.success("登录成功")));
}
}else {
httpServletResponse.sendRedirect(StrUtil.format("login?targetUrl={}", targetUrl));
}
}
/**
* 退出登录 == 命令浏览器删除cookie
*
* @return
*/
@GetMapping("logout")
public String logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,ModelAndView modelAndView) {
httpServletRequest.getCookies();
Cookie cookie = new Cookie("credentials", null);
cookie.setPath("/");
cookie.setMaxAge(0);
httpServletResponse.addCookie(cookie);
Cookie cookie2 = new Cookie("userName", null);
cookie2.setPath("/");
cookie2.setMaxAge(0);
httpServletResponse.addCookie(cookie);
httpServletResponse.addCookie(cookie2);
return "redirect:/user/login";
}
}
ArticleCategoryController.java
@RestController
@RequestMapping("article-category")
public class ArticleCategoryController {
@Autowired
ArticleCategoryService articleCategoryService;
@DeleteMapping("one/{id}")
public CommonHttpResult<String> deleteById(@PathVariable("id") String id) {
boolean deleteFlag = articleCategoryService.removeById(id, false);
return deleteFlag?CommonHttpResult.success(id) : CommonHttpResult.success();
}
}
login.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<script src="/js/jquery.min.js">script>
head>
<body>
<table border="1">
<form action="/user/login" method="post">
<tr>
<td>账号:td>
<td><input id="userName" name="userName" type="text">td>
<td><input style="display: none" id="targetUrl" name="targetUrl" type="text" value="${targetUrl!}">td>
tr>
<tr>
<td>密码:td>
<td><input id="password" name="password" type="text">td>
tr>
<tr>
<td colspan="2" style="text-align: center">
<button id="loginBtn" type="submit">登录button>
<button id="logoutBtn" type="button" ><a href="/user/logout">退出a>button>
td>
tr>
form>
table>
<h1>当前用户:<span id="currentUser">span>h1>
body>
<script>
$(function () {
function getCookie(cookie_name) {
var allcookies = document.cookie;
//索引长度,开始索引的位置
var cookie_pos = allcookies.indexOf(cookie_name);
// 如果找到了索引,就代表cookie存在,否则不存在
if (cookie_pos != -1) {
// 把cookie_pos放在值的开始,只要给值加1即可
//计算取cookie值得开始索引,加的1为“=”
cookie_pos = cookie_pos + cookie_name.length + 1;
//计算取cookie值得结束索引
var cookie_end = allcookies.indexOf(";", cookie_pos);
if (cookie_end == -1) {
cookie_end = allcookies.length;
}
//得到想要的cookie的值
var value = unescape(allcookies.substring(cookie_pos, cookie_end));
}
return value;
}
$("#currentUser").text(getCookie("userName"));
})
script>
html>
未登录前
访问:http://localhost:8080/user/login
访问:http://localhost:8080/article-category/one/c08d391e02bc11eb9416b42e99ea3e62
小知识点: 服务器校验用户账号、密码即登录成功,一般会将用户信息存入服务器的session中,每次客户端请求都会携带会话标识即所谓ID,服务端根据这个ID找到具体的session,如果session中含有用户信息说明这个请求是有权限进行访问,进行后续的业务处理,session中没找到用户信息直接响应报文返回报错或者401
登录后的请求响应 - 服务器根据sessionid判断他们的session会话是否有用户信息
application.yml
server:
port: 8080
# mybatisplus\u8BBE\u7F6E
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
cache-enabled: true
mapper-locations: classpath:mapper/*Mapper.xml
global-config:
db-config:
id-type: assign_uuid
logic-delete-value: 1
logic-not-delete-value: 0
logic-delete-field: is_del
where-strategy: not_empty #\u4E0Dwhere\u975Eempty\u7684\u5B57\u6BB5\u3010\u7A7A\u5B57\u7B26\u3001null\u503C\u3011
update-strategy: not_empty
insertStrategy: not_empty
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
username: root
password: root
url: jdbc:p6spy:mysql://localhost:3306/lrc_blog?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
freemarker:
suffix: .html
SessionAuthInterceptor.java
package work.linruchang.qq.mybaitsplusjoin.config.interceptor;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.stream.Stream;
/**
* 作用:cookie认证
*
* @author LinRuChang
* @version 1.0
* @date 2022/08/04
* @since 1.8
**/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SessionAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean authStatusFlag = false;
//登录成功时用户信息都存入内存无需从数据库拿取
HttpSession session = request.getSession();
Dict sessionUser = (Dict) session.getAttribute("user");
Cookie[] cookies = ObjectUtil.defaultIfNull(request.getCookies(), new Cookie[0]);
String cookieUserName = Stream.of(cookies)
.filter(cookie -> StrUtil.equals(cookie.getName(), "userName"))
.findFirst()
.map(Cookie::getValue)
.orElse(null);
if(ObjectUtil.isAllNotEmpty(sessionUser,cookieUserName)) {
if(StrUtil.equals(sessionUser.getStr("userName"), cookieUserName)) {
authStatusFlag = true;
}
}
//认证失败 == 重定向到登录页面
if (!authStatusFlag) {
StringBuffer targetUrlUrl = request.getRequestURL();
if (StrUtil.isNotBlank(request.getQueryString())) {
targetUrlUrl.append("?");
targetUrlUrl.append(request.getQueryString());
}
response.sendRedirect(StrUtil.format("/user2/login?targetUrl={}",targetUrlUrl));
}
return authStatusFlag;
}
}
MyConfig.java
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Autowired
SessionAuthInterceptor sessionAuthInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(sessionAuthInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/static/**","/user*/login/**","/user*/logout/**");
}
}
ArticleCategoryController.java
@RestController
@RequestMapping("article-category")
public class ArticleCategoryController {
@Autowired
ArticleCategoryService articleCategoryService;
/**
* 根据ID进行查询
* @param id
* @return
*/
@GetMapping("/one/{id}")
public CommonHttpResult<ArticleCategory> findById(@PathVariable("id") String id) {
return CommonHttpResult.success(articleCategoryService.getById(id));
}
}
UserController2.java
@Controller
@RequestMapping("user2")
public class UserController2 {
/**
* 登录页面
*
* @param httpServletRequest
* @param modelAndView
* @return
*/
@GetMapping("login")
public ModelAndView loginPage(HttpServletRequest httpServletRequest, ModelAndView modelAndView) {
modelAndView.setViewName("login2");
modelAndView.addObject("targetUrl", httpServletRequest.getParameter("targetUrl"));
return modelAndView;
}
/**
* 登录
*
* @param httpServletRequest
* @param httpServletResponse
* @param modelAndView
*/
@PostMapping("login")
@SneakyThrows
//public void login(@RequestBody Dict loginInfo, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, ModelAndView modelAndView) {
public void login(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, ModelAndView modelAndView) {
String userName = httpServletRequest.getParameter("userName");
String password = httpServletRequest.getParameter("password");
String targetUrl = httpServletRequest.getParameter("targetUrl");
if (StrUtil.equals(userName, "admin") && StrUtil.equals(password, "admin123")) {
//String targetUrl = loginInfo.getTargetUrl();
//自动会创建名字JSESSIONID的cookie用于标识会话
//将所有涉及用户的信息都存储进内存session中
HttpSession session = httpServletRequest.getSession();
session.setAttribute("user", Dict.create()
.set("userName", "admin")
.set("password", "admin123"));
//用户名-用于前端JS显示用户信息
Cookie cookie = new Cookie("userName", "admin");
cookie.setPath("/");
cookie.setMaxAge(-1); //会话cookie
httpServletResponse.addCookie(cookie);
if (StrUtil.isNotBlank(targetUrl)) {
httpServletResponse.sendRedirect(targetUrl);
} else {
httpServletResponse.setHeader("Content-Type", "application/json;charset=UTF-8");
httpServletResponse.getWriter().print(JSONUtil.toJsonStr(CommonHttpResult.success("登录成功")));
}
} else {
httpServletResponse.sendRedirect(StrUtil.format("login?targetUrl={}", targetUrl));
}
}
/**
* 退出登录 == 命令浏览器删除cookie
*
* @return
*/
@GetMapping("logout")
public String logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
//userName cookie过期
Cookie userNameCookie = new Cookie("userName", null);
userNameCookie.setPath("/");
userNameCookie.setMaxAge(0);
httpServletResponse.addCookie(userNameCookie);
//session会话过期
HttpSession session = httpServletRequest.getSession();
session.invalidate();
return "redirect:/user2/login";
}
}
login2.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<script src="/js/jquery.min.js">script>
head>
<body>
<table border="1">
<form action="/user2/login" method="post">
<tr>
<td>账号:td>
<td><input id="userName" name="userName" type="text">td>
<td><input style="display: none" id="targetUrl" name="targetUrl" type="text" value="${targetUrl!}">td>
tr>
<tr>
<td>密码:td>
<td><input id="password" name="password" type="text">td>
tr>
<tr>
<td colspan="2" style="text-align: center">
<button id="loginBtn" type="submit">登录button>
<button id="logoutBtn" type="button" ><a href="/user2/logout">退出a>button>
td>
tr>
form>
table>
<h1>当前用户:<span id="currentUser">span>h1>
body>
<script>
$(function () {
function getCookie(cookie_name) {
var allcookies = document.cookie;
//索引长度,开始索引的位置
var cookie_pos = allcookies.indexOf(cookie_name);
// 如果找到了索引,就代表cookie存在,否则不存在
if (cookie_pos != -1) {
// 把cookie_pos放在值的开始,只要给值加1即可
//计算取cookie值得开始索引,加的1为“=”
cookie_pos = cookie_pos + cookie_name.length + 1;
//计算取cookie值得结束索引
var cookie_end = allcookies.indexOf(";", cookie_pos);
if (cookie_end == -1) {
cookie_end = allcookies.length;
}
//得到想要的cookie的值
var value = unescape(allcookies.substring(cookie_pos, cookie_end));
}
return value;
}
$("#currentUser").text(getCookie("userName"));
})
script>
html>