一.概念
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()); }