建站教程,抖音权重查询工具,音乐APP网站开发,商业网点和商铺的区别文章目录 1. 认证授权概述1.1 认证授权概念1.1.1 认证1.1.2 授权 1.2 权限数据模型1.3 RBAC权限模型1.3.1 介绍1.3.2 基于角色访问控制1.3.3 基于资源访问控制 1.4 常见认证方式1.4.1 Cookie-Session1.4.2 jwt令牌无状态认证 1.5 技术实现 2. SpringSecurity入门2.1 介绍2.2 入… 文章目录 1. 认证授权概述1.1 认证授权概念1.1.1 认证1.1.2 授权 1.2 权限数据模型1.3 RBAC权限模型1.3.1 介绍1.3.2 基于角色访问控制1.3.3 基于资源访问控制 1.4 常见认证方式1.4.1 Cookie-Session1.4.2 jwt令牌无状态认证 1.5 技术实现 2. SpringSecurity入门2.1 介绍2.2 入门2.2.1 工程搭建2.2.2 认证配置【1】自定义合法登录用户信息 2.2.3 授权配置 2.3 密码加密2.3.1 可逆加密算法2.3.2 不可逆加密算法2.3.3 MD5与Bcrypt 2.4 程序完善2.4.1 密码加密处理2.4.2 动态查询用户 2.5 认证原理分析(了解) 1. 认证授权概述
1.1 认证授权概念
1.1.1 认证
在互联网中我们每天都会使用到各种各样的APP和网站在使用过程中通常还会遇到需要注册登录的情况输入你的用户名和密码才能正常使用也就是说成为这个应用的合法身份才可以访问应用的资源这个过程就是认证。认证是为了保护系统的隐私数据与资源用户的身份合法方可访问该系统的资源。
当然认证的方式有很多常见的账号密码登录手机验证码登录指纹登录刷脸登录等等。
简单说: 认证就是让系统知道我们是谁。
1.1.2 授权
认证是为了保护身份的合法性授权则是为了更细粒度的对数据进行划分授权是在认证通过的前提下发生的。控制不同的用户能够访问不同的资源。
授权是用户认证通过后根据用户的权限来控制用户访问资源的过程拥有资源的访问权限则正常访问没有权限则拒绝访问。
例如视频网站的VIP用户可以查看到普通用户看不到的资源信息。
1.2 权限数据模型
授权过程中我们需要知道如何对用户访问的资源进行控制需要了解一些简单的授权数据模型。
授权可以非常简单的理解成谁Who对什么What进行怎么样How的操作。
名词含义备注Who主体(Subject)一般指用户也可以是应用程序What资源(Resource)例如商品信息订单信息页面按钮或程序中的接口等信息How权限(Permission)规定了用户或程序对资源操作的许可。例如普通用户只能查看订单管理员可修改或删除订单这是因为普通用户和管理员用户对订单资源的操作权限不一样。
1). 主体、资源、权限的关系图 主体、资源、权限相关的数据模型如下
A. 主体用户id、账号、密码、…
B. 资源资源id、资源名称、访问地址、…
C. 权限权限id、权限标识、权限名称、资源id、…
D. 主体用户和权限关系用户id、权限id、…
2). 主体、资源、权限的表结构关系 你会发现权限中包含了一个资源ID多个权限可指向一个资源我们是否可以直接在权限信息中把资源信息包含进来呢当然这也是很多企业开发中的做法将权限和资源合并为 权限(权限ID、权限标识、权限名称、资源名称、资源访问地址、…) 1.3 RBAC权限模型
1.3.1 介绍
如何实现授权业界通常基于RBAC模型Role-Based Access Control - 基于角色的访问控制实现授权。 RBAC认为授权实际就是who,what,how三者之间的关系(3W)即who对 what 进行 how 的操作。
1.3.2 基于角色访问控制
RBAC基于角色的访问控制Role-Based Access Control是按角色进行授权比如主体的角色为总经理可以查询企业运营报表查询员工工资信息等访问控制流程如下 根据上图中的判断逻辑授权代码可表示如下
if(主体.hasRole(总经理角色标识)){//查询工资
}else{//权限不足
}如果上图中查询工资所需要的角色变化为总经理和部门经理此时就需要修改判断逻辑为“判断用户的角色是否是 总经理或部门经理”修改代码如下
if(主体.hasRole(总经理角色标识) || 主体.hasRole(部门经理角色标识)){ //查询工资
}else{//权限不足
}根据上边的例子发现当需要修改角色的权限时就需要修改授权的相关代码系统可扩展性差。
1.3.3 基于资源访问控制
RBAC基于资源的访问控制(Resource-Based Access Control)是按资源(或权限)进行授权。 同样是上面的需求这时候我们的代码变成了
if(Subject.hasPermission(查询员工工资的权限标识)){// 查询员工工资
}优点系统设计时定义好查询工资的权限标识即使查询工资所需要的角色变化为总经理和部门经理也不需要修授权代码系统可扩展性强。
1.4 常见认证方式
认证(登录)几乎是任何一个系统的标配web 系统、APP、PC客户端等都需要注册、登录、授权。
1.4.1 Cookie-Session
早期互联网以 web 为主客户端是浏览器所以 Cookie-Session 方式最那时候最常用的方式直到现在一些 web 网站依然用这种方式做认证。
认证过程大致如下
A. 用户输入用户名、密码或者用短信验证码方式登录系统
B. 服务端验证后创建一个 Session 记录用户登录信息 并且将 SessionID 存到 cookie响应回浏览器
C. 下次客户端再发起请求自动带上 cookie 信息服务端通过 cookie 获取 Session 信息进行校验 弊端
只能在 web 场景下使用如果是 APP 中不能使用 cookie 的情况下就不能用了即使能在 web 场景下使用也要考虑跨域问题因为 cookie 不能跨域域名或者ip一致端口号一致协议要一致cookie 存在 CSRF跨站请求伪造的风险如果是分布式服务需要考虑 Session 同步同步问题
1.4.2 jwt令牌无状态认证
JSON Web TokenJWT-字符串是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
认证过程:
A. 依然是用户登录系统
B. 服务端验证并通过指定的算法生成令牌返回给客户端;
C. 客户端拿到返回的 Token存储到 local storage/Cookie中
D. 下次客户端再次发起请求将 Token 附加到 header 中
E. 服务端获取 header 中的 Token 通过相同的算法对 Token 进行验证如果验证结果相同则说明这个请求是正常的没有被篡改。这个过程可以完全不涉及到查询 Redis 或其他存储 优点
A. 使用 json 作为数据传输有广泛的通用型并且体积小便于传输
B. 不需要在服务器端保存相关信息节省内存资源的开销
C. jwt 载荷部分可以存储业务相关的信息非敏感的例如用户信息、角色等
1.5 技术实现
技术概述Apache ShiroApache旗下的一款安全框架SpringSecuritySpring家族的一部分, Spring体系中提供的安全框架, 包含认证、授权两个大的部分CASCAS是一个单点登录(SSO)服务开始是由耶鲁大学的一个组织开发后来归到apereo去管自行实现自行通过业务代码实现, 实现繁琐, 代码量大
2. SpringSecurity入门
2.1 介绍
Spring Security是为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架(包含: 认证 , 授权两个方面)。它提供了完整的安全性解决方案可以在Web请求级别和方法调用级别处理身份认证和授权充分利用了Spring IOCDI控制反转Inversion of Control ,DI:Dependency Injection 依赖注入和AOP面向切面编程功能为应用系统提供声明式的安全访问控制功能。
官网地址
2.2 入门
2.2.1 工程搭建
创建测试工程并引入依赖 ;
A. pom.xml
parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.4.RELEASE/versionrelativePath/
/parentdependencies!-- web起步依赖 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- springBoot整合Security --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependency!-- lombok --dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependency/dependenciesB. 引导类
SpringBootApplication
public class StudySecurityApplication{public static void main(String[] args) {SpringApplication.run(StudySecurityApplication.class,args);}
}C. Controller
RestController
RequestMapping(/hello)
public class HelloController {GetMapping(/{msg})public String Hello(PathVariable(msg) String msg){return hello: msg;}GetMapping(/hello)public String hello(){return hello security;}GetMapping(/say)public String say(){return say security;}}D. 测试
访问: http://localhost:8080/hello/snow
会自动拦截并跳转到登录页面SpringSecurity提供登录之后才可以访问 而登录的用户名和密码都是SpringSecurity中内置的默认的用户名密码 用户名为 user 密码为控制台输出的一段随机数 (如下) 效果 登录成功之后会自动跳转到之前访问的地址 注意
# 我们也可在配置文件中配置用户名和密码实际开发中密码不应明文配置
spring:security:user:name: adminpassword: 123456再次重启我们就用配置的用户和密码登录了
2.2.2 认证配置
【1】自定义合法登录用户信息
上述的入门程序中, 用户名密码是框架默认帮我们生成的, 我们并没有指定, 如果我们想指定系统的访问用户名及密码, 可以通过配置的形式声明 , 声明一个 UserDetailsService 类型的Bean。
Configuration
// 开启web安全设置生效
EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 构建认证服务并将对象注入spring IOC容器用户登录时会调用该服务进行用户合法信息认证* return*/Beanpublic UserDetailsService userDetailsService(){// 定义加载用户信息的服务InMemoryUserDetailsManager inMemoryUserDetailsManager new InMemoryUserDetailsManager();// 构建用户UserDetails u1 User.withUsername(admin).password({noop}123456).authorities(P10, ROLE_ADMIN).build();UserDetails u2 User.withUsername(snow).password({noop}123456).authorities(O10, ROLE_USER).build();inMemoryUserDetailsManager.createUser(u1);inMemoryUserDetailsManager.createUser(u2);return inMemoryUserDetailsManager;}
}在 userDetailsService() 方法中 我们返回了一个UserDetailsService 给 spring 容器Spring Security 会使用它来获取用户信息。我们暂时使用 InMemoryUserDetailsManager 实现类并在其中分别创建了 admin、snow 两个用户并设置密码和权限。
2.2.3 授权配置
1). 编码方式
Configuration
EnableWebSecurity
EnableGlobalMethodSecurity(prePostEnabled true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {Beanpublic UserDetailsService userDetailsService(){InMemoryUserDetailsManager inMemoryUserDetailsManager new InMemoryUserDetailsManager();inMemoryUserDetailsManager.createUser(User.withUsername(admin).password({noop}123456).authorities(P10,ROLE_ADMIN).build());inMemoryUserDetailsManager.createUser(User.withUsername(snow).password({noop}123456).authorities(O10,ROLE_USER).build());return inMemoryUserDetailsManager;}Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().and().logout().and().csrf().disable().authorizeRequests().antMatchers(/register).permitAll() //不登录即可访问.antMatchers(/hello).hasAuthority(P1) //具有P1权限才可以访问.antMatchers(/say).hasRole(USER) //具有SELLER 角色才可以访问.anyRequest().authenticated(); //其他的登录之后就可以访问}
}CSRFCross-site request forgery跨站请求伪造也被称为One Click Attack或者 Session Riding通常缩写为 CSRF 或者 XSRF是一种对网站的恶意利用。 再次重启项目 再访问就需要带相应的权限了
权限不足 如下 2). 注解方式
在控制方法 /URL 的权限时, 可以通过配置类中配置的方式进行控制, 也可以使用 注解 PreAuthorize 来进行控制, 推荐使用注解: GetMapping(/hello)PreAuthorize(hasAuthority(P10))public String hello(){return hello security;}GetMapping(/say)
PreAuthorize(hasRole(USER))
public String say(){return say security;
}使用PreAuthorize,需要开启全局方法授权开关,加上注解EnableGlobalMethodSecurity(prePostEnabledtrue)
经过上述的入门程序的演示我们对于SpringSecurity的基本使用有了一定的了解但是在入门程序中存在两个问题
A. 密码采用的是明文的不安全 ;
B. 用户名/密码直接通过程序硬编码不够灵活 2.3 密码加密
2.3.1 可逆加密算法
加密后, 密文可以反向解密得到密码原文;
1). 对称加密
指加密和解密使用相同密钥的加密算法。
优点: 对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。 缺点: 没有非对称加密安全。
常见的对称加密算法DES、3DES、DESX、Blowfish、RC4、RC5、RC6和AES
2). 非对称加密
指加密和解密使用不同密钥的加密算法也称为公私钥加密。假设两个用户要加密交换数据双方交换公钥使用时一方用对方的公钥加密另一方即可用自己的私钥解密。
加密和解密:
私钥加密持有私钥或公钥才可以解密公钥加密持有私钥才可解密
优点: 非对称加密与对称加密相比其安全性更好 缺点: 非对称加密的缺点是加密和解密花费时间长、速度慢只适合对少量数据进行加密。
2.3.2 不可逆加密算法
一旦加密就不能反向解密得到密码原文 。通常用于密码数据加密。
常见的不可逆加密算法有 MD5 、SHA、HMAC
2.3.3 MD5与Bcrypt
1).MD5
MD5是比较常见的加密算法广泛的应用于软件开发中的密码加密通过MD5生成的密文是无法解密得到明文密码的。但是现在在大数据背景下很多的网站通过大数据可以将简单的MD5加密的密码破解。 网址 https://www.cmd5.com/
可以在用户注册时限制用户输入密码的长度及复杂度从而增加破解难度。
2). Bcrypt
用户表的密码通常使用 MD5 等不可逆算法加密后存储为防止彩虹表破解会先使用一个特定的字符串如域名加密然后再使用一个随机的 salt盐值加密。 特定字符串是程序代码中固定的salt 是每个密码单独随机一般给用户表加一个字段单独存储比较麻烦。
BCrypt 算法将 salt 随机并混入最终加密后的密码验证时也无需单独提供之前的salt从而无需单独处理 salt 问题。
加密密码:
BCryptPasswordEncoder bCryptPasswordEncoder new BCryptPasswordEncoder();
for (int i 0; i 10; i) {System.out.println(bCryptPasswordEncoder.encode(123456));
}得到结果:
$2a$10$MLJ9f5jViB3rRsQTtxLclumRsK5mdhXsMxe9YBUMyaorTHKwgUEsW
$2a$10$TTAKm8GHEHahRtNDVj8jjeuuJ2AnDaEORHO.3qbeXEpQ8YboPyV3C
$2a$10$wydZqWCBuppICk3YvCw/pOGHVuwtp8qCPCZ1aRjr6904vKXtGnjI6
$2a$10$Cu4ckrKXfTOITh5rl4dLx.h59Gtfn9kdaayVfO/d//4avMQrbTHMe
$2a$10$SKpM1IHOvIUgPTuvTlAtEu9mxANFJUiKc2YNMVQY.Y2.fFVtAdpuG
$2a$10$fKBz3tBR4vt02afJscNa0ulPE.V0/DdNYrv.miuou4mo8vuDIC27u
$2a$10$IludxV8mcTLMnm0wEAOinuvYnJmzCUbvrcwsoSTRw3l9JBXuvSM1q
$2a$10$yAvHC4KobuuPMP10Z9XYh.jXo6y0orDsKSH6xFVe3oZlWiNhDNg/6
$2a$10$WBPYtRG19HkPDtUUJK57r.NjHQ/xocucNfZuinwZQlddFdxEiejoG
$2a$10$YZOzdhz03TMagK4xJRpF7ek51EWNkereo0nu01GRZjfJsKuxf0hEG 验证密码:
BCryptPasswordEncoder bCryptPasswordEncoder new BCryptPasswordEncoder();
boolean matches bCryptPasswordEncoder.matches(123456, $2a$10$c2sZT/LtM1ExWfZjO0yIPeTGSqMSlX7oi.SvliMbeZpT9Y4qIBDue);
System.out.println(matches);//返回值为true, 则代表验证通过; 反之, 验证不通过2.4 程序完善
2.4.1 密码加密处理
在配置类 SecurityConfig中配置Bean:
//配置密码加密器 ;
Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){return new BCryptPasswordEncoder();
}//配置认证信息 , 密码使用BCryptPasswordEncoder加密 ;
Bean
public UserDetailsService userDetailsService(){InMemoryUserDetailsManager inMemoryUserDetailsManager new InMemoryUserDetailsManager();inMemoryUserDetailsManager.createUser(User.withUsername(admin).password($2a$10$qcKkkvsoClF9tO8c9wlR/ebgU8VM39GP5ZUdsts.XSPDmE40l.BP2).authorities(P10,ROLE_ADMIN).build());inMemoryUserDetailsManager.createUser(User.withUsername(snow).password($2a$10$qcKkkvsoClF9tO8c9wlR/ebgU8VM39GP5ZUdsts.XSPDmE40l.BP2).authorities(O10,ROLE_USER).build());return inMemoryUserDetailsManager;
}2.4.2 动态查询用户
上述的案例中, 用户名密码都是在代码中写死的, 现在实际项目中, 是需要动态从数据库查询;简易的数据库表如下:
create database security_demo default charsetutf8mb4;
use security_demo;CREATE TABLE tb_user (id int(11) NOT NULL AUTO_INCREMENT,username varchar(100) DEFAULT NULL,password varchar(100) DEFAULT NULL,roles varchar(100) DEFAULT NULL,PRIMARY KEY (id)
) ENGINEInnoDB DEFAULT CHARSETutf8mb4;INSERT INTO tb_user VALUES (1, admin, $2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC, ROLE_ADMIN,P10);
INSERT INTO tb_user VALUES (2, snow, $2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC, ROLE_USER,O10);A. pom.xml dependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion2.1.4/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependencyB. application.yml 略略略… C. 实体类 Data
NoArgsConstructor
AllArgsConstructor
Builder
public class TbUser implements Serializable {/*** */private Integer id;/*** */private String username;/*** */private String password;/*** */private String roles;private static final long serialVersionUID 1L;
}D. mapper 略略略… D. 自定义UserDetailsService
package com.sss.security.config;import com.sss.security.mapper.TbUserMapper;
import com.sss.security.pojo.TbUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;import java.util.List;/*** Description*/
Component
public class UserDetailsServiceImpl implements UserDetailsService {Autowiredprivate TbUserMapper tbUserMapper;Overridepublic UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {TbUser user tbUserMapper.findByUserName(userName);if (usernull) {throw new UsernameNotFoundException(用户不存在);}//构建认证明细对象//获取用户权限ListGrantedAuthority list AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles());User user1 new User(user.getUsername(),user.getPassword(),list);return user1;}
}UserDetails是一个接口,User是该接口的实现类,封装用户的数据及用户的权限数据, 注意不要导错包 ;
在SecurityConfig中注释掉inMemoryUserDetailsManager bean并配置加密bean Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}整体访问流程如下 2.5 认证原理分析(了解)
Spring Security所解决的问题就是安全访问控制而安全访问控制功能其实就是对所有进入系统的请求进行拦截校验每个请求是否能够访问它所期望的资源。根据前边知识的学习可以通过Filter或AOP等技术来实现Spring Security对Web资源的保护是靠Filter实现的所以从这个Filter来入手逐步深入Spring Security原理。 当初始化Spring Security时会创建一个名为 SpringSecurityFilterChain的Servlet过滤器类型为 org.springframework.security.web.FilterChainProxy它实现了javax.servlet.Filter因此外部的请求会经过此 类下图是Spring Security过虑器链结构图 FilterChainProxy是一个代理真正起作用的是FilterChainProxy中SecurityFilterChain所包含的各个Filter同时 这些Filter作为Bean被Spring管理它们是Spring Security核心各有各的职责但他们并不直接处理用户的认 证也不直接处理用户的授权而是把它们交给了认证管理器 AuthenticationManager和决策管理器 AccessDecisionManager进行处理。
下面介绍过滤器链中主要的几个过滤器及其作用
SecurityContextPersistenceFilter 这个Filter是整个拦截过程的入口和出口也就是第一个和最后一个拦截 器会在请求开始时从配置好的 SecurityContextRepository 中获取SecurityContext然后把它设置给 SecurityContextHolder。在请求完成后将SecurityContextHolder 持有的 SecurityContext 再保存到配置好 的SecurityContextRepository同时清除 securityContextHolder 所持有的 SecurityContextUsernamePasswordAuthenticationFilter用于处理来自表单提交的认证。该表单必须提供对应的用户名和密 码其内部还有登录成功或失败后进行处理的 AuthenticationSuccessHandler 和 AuthenticationFailureHandler这些都可以根据需求做相关改变FilterSecurityInterceptor 是用于保护web资源的使用AccessDecisionManager对当前用户进行授权访问前面已经详细介绍过了ExceptionTranslationFilter 能够捕获来自 FilterChain 所有的异常并进行处理。但是它只会处理两类异常 AuthenticationException 和 AccessDeniedException其它的异常它会继续抛出。
接处理用户的认 证也不直接处理用户的授权而是把它们交给了认证管理器 AuthenticationManager和决策管理器 AccessDecisionManager进行处理。