一.概念
1.认证
认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。
用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。
2.会话
用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为了保持当前用户的登录状态所提供的机制,常见的有基于 session 方式、基于 token 方式等。
基于 session 的认证方式如下图:

它的交互流程是,用户认证成功后,在服务端生成用户相关的数据保存在 session (当前会话)中,发给客户端的 sesssion_id 存放到 cookie 中,这样用户客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户的合法校验,当用户退出系统或 session 过期销毁时,客户端的 session_id 也就无效了。
基于token方式如下图:

它的交互流程是,用户认证成功后,服务端生成一个 token 发给客户端,客户端可以放到 cookie 或 localStorage 等存储中,每次请求时带上 token,服务端收到token通过验证后即可确认用户身份。
基于 session 的认证方式由 Servlet 规范定制,服务端要存储 session 信息需要占用内存资源,客户端需要支持 cookie;基于 token 的方式则一般不需要服务端存储 token,并且不限制客户端的存储方式。如今移动互联网时代更多类型的客户端需要接入系统,系统多是采用前后端分离的架构进行实现,所以基于 token 的方式更适合。
3.授权
授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。
授权的数据模型
授权可简单理解为Who对What(which)进行How操作,包括如下:
Who,即主体(Subject),主体一般是指用户,也可以是程序,需要访问系统中的资源。 What,即资源(Resource),如系统菜单、页面、按钮、代码方法、系统商品信息、系统订单信息等。系统菜单、页面、按钮、代码方法都属于系统功能资源,对于web系统每个功能资源通常对应一个URL;系统商品信息、系统订单信息都属于实体资源(数据资源),实体资源由资源类型和资源实例组成,比如商品信息为资源类型,商品编号 为001的商品为资源实例。How,权限/许可(Permission),规定了用户对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个代码方法的调用权限、编号为001的用户的修改权限等,通过权限可知用户对哪些资源都有哪些操作许可。
主体、资源、权限相关的数据模型如下:
主体(用户id、账号、密码、…)
权限(权限id、权限标识、权限名称、资源名称、资源访问地址、…)
角色(角色id、角色名称、…)
角色和权限关系(角色 id、权限id、…)
主体(用户)和角色关系(用户id、角色id、…)

4.RBAC
1)基于角色的访问控制
RBAC基于角色的访问控制(Role-Based Access Control)是按角色进行授权,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等,访问控制流程如下:

2)基于资源的访问控制
按资源进行授权(Resource-Based Access Controller)

5.基于Session的认证方式
基于 Session 认证方式的流程是,用户认证成功后,在服务端生成用户相关的数据保存在 session (当前会话),而发给客户端的 sesssion_id 存放到 cookie 中,这样用客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户的合法校验。当用户退出系统或 session 过期销毁时,客户端的 session_id 也就无效了。
下图是 session 认证方式的流程图:

基于 Session 的认证机制由 Servlet 规范定制,Servlet 容器已实现,用户通过 HttpSession 的操作方法即可实现,如下是 HttpSession 相关的操作 API 。
|
方法 |
含义 |
|
HttpSession getSession(Boolean create) |
获取当前HttpSession对象 |
|
void setAttribute(String name,Object value) |
向session中存放对象 |
|
object getAttribute(String name) |
从session中获取对象 |
|
void removeAttribute(String name) |
移除session中对象 |
|
void invalidate() |
使HttpSession失效 |
二.使用 Spring Security
1.什么是Spring Security
Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。

SpringSecurity的核心功能:
用户认证(Authentication):系统判断用户是否能登录
用户授权(Authorization):系统判断用户是否有权限去做某些事情
SpringSecurity 特点:
Spring 技术栈的组成部分,与Spring 无缝整合。
全面的权限控制,能提供完整可扩展的认证和授权支持保护
专门为 Web 开发而设计。
重量级,需要引入各种家族组件与依赖
2.使用SpringSecurity
1)创建springboot项目
引入jir包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>

2)创建测试类

3)访问
访问主页会被拦截 , 登录后放行

默认账号为user
默认密码在后端控制台

登录后 访问成功

3.自定义用户名和密码
1)在配置文件设置用户信息
spring.security.user.name=test spring.security.user.password=123456

登录

访问成功

2)在内存中定义用户信息
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* AuthenticationManagerBuilder 认证管理器的构建者
* @param auth
* @throws Exception
*/
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// // UserDeatils
// UserDetails user = User.withDefaultPasswordEncoder() // 默认不支持明文密码 加密 使用默认的加密方式
// .username("test") // 用户名 user
// .password("123456")// 密码 123456
// .roles("USER")
// .build();
// auth.inMemoryAuthentication() // inMemoryAuthentication 内存 // 将用户的信息 存放到内存里面
// .withUser(user);// user
// }
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// dgjsgfhgfwkhsabvksghsgksgkws -> 123456
//bCryptPasswordEncoder.encode("123456") == dgjsgfhgfwkhsabvksghsgksgkws
//不支持 明文
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String encode = bCryptPasswordEncoder.encode("123456");
System.out.println(encode);
auth.inMemoryAuthentication()
.passwordEncoder(bCryptPasswordEncoder)
.withUser("test")
.password(encode)
.roles("USER");
}
}

或者

也可以储存多个用户

登录成功

4.自定义页面
1)创建login页面
<form action="/test" method="post"> 用户名:<input type="text" name="myname"> <br/> 用户名:<input type="password" name="mypwd"> <br/> <input type="submit" value="login"> </form>
注意 请求方法为post

2)修改配置类
//自定义登录
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置登录表单 路径前必须加 "/"
http.formLogin().loginPage("/login.html")//登录的页面
.loginProcessingUrl("/test")
.defaultSuccessUrl("/test");//默认路径
// .usernameParameter("myname") .passwordParameter("mypwd") //自定义密码和账号
http.authorizeHttpRequests().antMatchers("/login.html","/userlogin","/").permitAll();//表示放行
//除了以上三个路径其他的路径都需要认证
http.authorizeHttpRequests().anyRequest().authenticated();
//csrf 方便html文件通过
http.csrf().disable();
}

登录页面改变

参数

表示配置资源信息的时候角色一定是以ROLE_为开始的
![]()
5.使用注解
1)修改启动类
@EnableGlobalMethodSecurity(securedEnabled=true,prePostEnabled = true,jsr250Enabled=true)

2)判断是否有对象
@Secured({"ROLE_admin"})
@RequestMapping("testSecured")
public String testSecured() {
return "testSecured";
}
表示用户拥有ROLE_ADMIN角色时才允许访问

以上方法是将用户信息存储到内存中
6.自定义实现类完成登录
1)实现 UserDetailsService 接口

2)获取用户信息

3)操作
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encode = passwordEncoder.encode("123456");
TabUser tabUser= new TabUser("admin1",encode);
if(username.equals(tabUser.getUsername())){
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
// authorities 代表的是所有的资源的信息
// 菜单 角色
// 如果是角色 ROLE_
// 菜单 随便写
authorities.add(new SimpleGrantedAuthority("testpath"));// 菜单
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));// 角色
return new User(username,encode,authorities);
}

4)修改配置文件
@Resource
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder getPassword(){
return new BCryptPasswordEncoder();
}
// 自定义用户的信息
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(getPassword());
}

278





