手撸一个springsecurity,了解一下security原理

转载自:www.javaman.cn 手撸一个springsecurity,了解一下security原理

今天手撸一个简易版本的springsecurity,带大家理解下springsecurity的原理:

安全框架的两个特点就是认证和授权,让我们来通过代码了解下认证和授权的处理方式:

1、认证

认证就是指需要登录才能进行系统操作,如:登录qq、微信或者是web系统的登录都是认证的过程

1.1 工程目录

1.2 maven配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dashi</groupId>
<artifactId>security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>security</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build> </project>

1.3 application.yml

spring:
mvc:
view:
prefix: /
suffix: .html

1.4 创建User,模拟springsecurity的用户信息的核心接口UserDetails

import lombok.AllArgsConstructor;
import lombok.Data; @Data
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
}

1.5 创建AuthenticationService,模拟springsecurity的UserDetailsService核心接口

public interface AuthenticationService {
public User authentication(AuthenticationRequest authenticationRequest);
}

1.6 实现AuthenticationService,模拟实现UserDetailsService的过程

@Service
public class AuthenticationServiceImpl implements AuthenticationService {
private Map<String,User> users = new HashMap<String,User>();
@Override
public User authentication(AuthenticationRequest authenticationRequest) {
if(authenticationRequest == null||"".equals(authenticationRequest.getUsername())||"".equals(authenticationRequest.getPassword())){
throw new RuntimeException("用户名或密码为空");
}
User user = users.get(authenticationRequest.getUsername());
if(user == null){
throw new RuntimeException("用户不存在");
}
if(!user.getPassword().equals(authenticationRequest.getPassword())){
throw new RuntimeException("密码不正确");
}
return user; //模拟实现UserDetailS的User
} public User getUser(String username){
return users.get(username);
}
//创建两个账户,模拟管理员和普通的来宾用户
{
users.put("admin",new User(1,"admin","123")); //管理员
users.put("guest",new User(2,"guest","111")); //来宾账户
}
}

1.7 接收请求参数封装对象

@Data
public class AuthenticationRequest {
private String username;
private String password;
}

1.8 登录Controller,对/login请求处理,它调用AuthenticationService完成认证并返回登录结果提示信息

@Controller
public class LoginController { @Autowired
private AuthenticationService authenticationService; @GetMapping("/login")
public String login(){
return "login";
} @PostMapping(value = "/login",produces = {"text/plain;charset=UTF-8"})
public String login(AuthenticationRequest authenticationRequest, Model model){
System.out.println("111"+authenticationRequest);
User user = authenticationService.authentication(authenticationRequest);
String loginMsg = user.getUsername()+"登录成功";
System.out.println(loginMsg);
model.addAttribute("loginMsg",loginMsg);
return "loginsuccess";
}
}

1.9 认证页面

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>登录</title>
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录1">
</form>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Title</title>
</head>
<body>
<h1 th:text="${loginMsg}"></h1>
</body>
</html>

1.10 认证成功后打印登录成功

1)登录界面如下

2)密码错误界面

3)登录成功界面

2、授权

授权就是控制什么样的用户或者角色访问什么功能,例如:管理员可以访问全部功能,guest普通用户只能访问某一个菜单或者功能

2.1 User增加权限authorities和session

package com.dashi.security.model;

import lombok.AllArgsConstructor;
import lombok.Data; import java.util.Set; @Data
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
//登录用户,增加登录用户session
public static final String LOGIN_USER = "user";
/**
* 用户权限
*/
private Set<String> authorities;
}

2.2 用户增加角色authorities

import com.dashi.security.model.AuthenticationRequest;
import com.dashi.security.model.User;
import com.dashi.security.service.AuthenticationService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set; @Service
public class AuthenticationServiceImpl implements AuthenticationService {
private Map<String,User> users = new HashMap<String,User>();
@Override
public User authentication(AuthenticationRequest authenticationRequest) {
if(authenticationRequest == null||"".equals(authenticationRequest.getUsername())||"".equals(authenticationRequest.getPassword())){
throw new RuntimeException("用户名或密码为空");
}
User user = users.get(authenticationRequest.getUsername());
if(user == null){
throw new RuntimeException("用户不存在");
}
if(!user.getPassword().equals(authenticationRequest.getPassword())){
throw new RuntimeException("密码不正确");
}
return user;
} public User getUser(String username){
return users.get(username);
} {
//增加管理员权限
Set<String> authorities1 = new HashSet<>();
authorities1.add("admin");
//增加来宾权限
Set<String> authorities2 = new HashSet<>();
authorities2.add("guest");
users.put("admin",new User(1,"admin","123",authorities1));
users.put("guest",new User(2,"guest","111",authorities2));
}
}

2.3登录成功后,将用户放到session中

import com.dashi.security.model.AuthenticationRequest;
import com.dashi.security.model.User;
import com.dashi.security.service.AuthenticationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpSession; @Controller
public class LoginController { @Autowired
private AuthenticationService authenticationService; @GetMapping("/login")
public String login(){
return "login";
} @PostMapping(value = "/login",produces = {"text/plain;charset=UTF-8"})
public String login(AuthenticationRequest authenticationRequest, Model model, HttpSession session){
System.out.println("111"+authenticationRequest);
User user = authenticationService.authentication(authenticationRequest);
session.setAttribute(User.LOGIN_USER,user);
String loginMsg = user.getUsername()+"登录成功";
System.out.println(loginMsg);
model.addAttribute("loginMsg",loginMsg);
return "loginsuccess";
}
}

2.4 增加Springboot拦截器配置,判断是admin用户,可以访问所有资源resource1和resource2,如果是guest用户只允许访问resource2资源

import com.dashi.security.model.User;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter; @Component
public class MyAuthenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object object = request.getSession().getAttribute(User.LOGIN_USER);
System.out.println("object = " + object);
if(object == null){
writeContent(response,"请登录");
}
//获取请求的url
String requestURI = request.getRequestURI();
User user = (User)object;
if(user.getAuthorities().contains("admin") && requestURI.contains("/resource1") || requestURI.contains("/resource2")){
writeContent(response,user.getUsername()+"访问:"+requestURI+"访问成功!");
return true;
}
if(user.getAuthorities().contains("guest") && requestURI.contains("/resource2")){
writeContent(response,user.getUsername()+"访问:"+requestURI+"访问成功!");
return true;
}
writeContent(response,"权限不足!");
return false;
} private void writeContent(HttpServletResponse response,String msg) throws IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter printWriter = response.getWriter();
printWriter.write(msg);
printWriter.close();
response.resetBuffer();
}
}

2.5 拦截器进行请求拦截,拦截/resource/**请求

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration
public class LoginConfig implements WebMvcConfigurer {
@Autowired
private MyAuthenInterceptor myAuthenInterceptor; public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(myAuthenInterceptor).addPathPatterns("/resource/**");
}
}

2.6 授权测试界面如下:

1)登录成功后设置resource1和resource2访问功能

2)admin用户访问资源1和资源2





3)guest用户只能访问资源2,不能访问资源1



2.7 实现了springsecurity的认证和授权

1、 认证功能:

loginPage("/login.html")即为认证

2、 授权功能:

.antMatchers("/resource/resource1").hasAuthority(“admin”)

.antMatchers("/resource/resource2").hasAuthority(“admin”)

.antMatchers("/resource/resource2").hasAuthority(“guest”)

  @Override
protected void configure(HttpSecurity http) throws Exception {
//以下五步是表单登录进行身份认证最简单的登录环境
http.formLogin() //表单登陆
.loginPage("/login.html") //指定登陆页面
.loginProcessingUrl("/authentication/form")//登陆页面提交的页面 开始使用UsernamePasswordAuthenticationFilter过滤器处理请求
.and() //
.authorizeRequests() //下面的都是授权的配置
.antMatchers("/resource/resource1").hasAuthority("admin")
.antMatchers("/resource/resource2").hasAuthority("admin") .antMatchers("/resource/resource2").hasAuthority("guest")
.and()
.csrf().disable();//关闭跨站请求伪造攻击拦截

手撸一个springsecurity,了解一下security原理的更多相关文章

  1. 手撸一个SpringBoot-Starter

    1. 简介 通过了解SpringBoot的原理后,我们可以手撸一个spring-boot-starter来加深理解. 1.1 什么是starter spring官网解释 starters是一组方便的依 ...

  2. 通过 Netty、ZooKeeper 手撸一个 RPC 服务

    说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...

  3. 使用Java Socket手撸一个http服务器

    原文连接:使用Java Socket手撸一个http服务器 作为一个java后端,提供http服务可以说是基本技能之一了,但是你真的了解http协议么?你知道知道如何手撸一个http服务器么?tomc ...

  4. 【手撸一个ORM】MyOrm的使用说明

    [手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...

  5. 第二篇-用Flutter手撸一个抖音国内版,看看有多炫

    前言 继上一篇使用Flutter开发的抖音国际版 后再次撸一个国内版抖音,大部分功能已完成,主要是Flutter开发APP速度很爽,  先看下图 项目主要结构介绍 这次主要的改动在api.dart 及 ...

  6. C#基于Mongo的官方驱动手撸一个Super简易版MongoDB-ORM框架

    C#基于Mongo的官方驱动手撸一个简易版MongoDB-ORM框架 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongo ...

  7. 五分钟,手撸一个Spring容器!

    大家好,我是老三,Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌. 这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开S ...

  8. Golang:手撸一个支持六种级别的日志库

    Golang标准日志库提供的日志输出方法有Print.Fatal.Panic等,没有常见的Debug.Info.Error等日志级别,用起来不太顺手.这篇文章就来手撸一个自己的日志库,可以记录不同级别 ...

  9. 以鶸ice为例,手撸一个解释器(一)明确目标

    代码地址 # HelloWorld.ice print("hello, world") 前言(废话) 其实从开始学习编译原理到现在已经有快半年的时间了,但是其间常常不能坚持看下去龙 ...

随机推荐

  1. c#多进程通讯,今天,它来了

    引言 在c#中,可能大多数人针对于多线程之间的通讯,是熟能生巧,对于AsyncLocal 和ThreadLocal以及各个静态类中支持线程之间传递的GetData和SetData方法都是信手拈来,那多 ...

  2. 学习笔记--Java中的变量

    Java中的变量 /** * 关于 Java 语言当中的变量: * * 1. 什么是变量? * - 变量的本质上来说是内存空间,这块空间有(数据类型.名字.字面值) * - 变量包括三部分:数据类型. ...

  3. 【爬虫】将 Scrapy 部署到 k8s

    一. 概述 因为学习了 docker 和 k8s ,不管什么项目都想使用容器化部署,一个最主要的原因是,使用容器化部署是真的方便.上一篇文章 [爬虫]从零开始使用 Scrapy 介绍了如何使用 scr ...

  4. Ubuntu16桌面版编译OpenCV4的java库和so库

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  5. vuecli学习01 - 环境搭建

    到这个链接下载nvm的安装包:https://github.com/coreybutler/nvm-windows/releases. 然后点击一顿下一步,安装即可! 安装完成后,还需要配置环境变量. ...

  6. golang中的tcp编程

    1. tcp server package main import ( "bufio" "fmt" "net" ) func main() ...

  7. 沁恒CH32F103C8T6(三): PlatformIO DAPLink和WCHLink下载配置

    目录 沁恒CH32F103C8T6(一): Keil5环境配置,示例运行和烧录 沁恒CH32F103C8T6(二): Linux PlatformIO环境配置, 示例运行和烧录 沁恒CH32F103C ...

  8. 基于 esbuild 的 universal bundler 设计

    --字节跳动前端 Byte FE :杨健 背景 由于 Lynx(公司自研跨端框架)编译工具和传统 Web 编译工具链有较大的差别(如不支持动态 style 和动态 script 基本告别了 bundl ...

  9. [luogu P1312]Mayan游戏

    其实就是一道锻炼码力的简单题-- 看到题目中的\(0<x\leqslant 5\)也就知道是爆搜了吧( 我们仿照写游戏的方法多写几个函数,能够有效降低错误率(确信 我们写出大致的搜索流程来: 如 ...

  10. 3. scala-spark wordCount 案例

    1. 创建maven 工程 2. 相关依赖和插件 <dependencies> <dependency> <groupId>org.apache.spark< ...