Spring Security
认证的概念
进入移动互联网时代,每天都在刷手机,常用的软件有微信、支付宝、头条等,拿微信来举例子说明认
证 相关的基本概念,在初次使用微信前需要注册成为微信用户,然后输入账号和密码即可登录微信,输
入账号和密码 登录微信的过程就是认证。
系统为什么要认证? 认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。
认证 :用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的
身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登
录,二维码登录,手 机短信登录,指纹认证等方式。
用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为
了保持当前 用户的登录状态所提供的机制,常见的有基于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的方式更适合。
Spring Security 快速上手
Spring Security介绍
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框
架。由于它 是Spring生态系统中的一员,因此它伴随着整个Spring生态系统不断修正、升级,在spring
boot项目中加入spring security十分方便,使用Spring Security 减少了为企业系统安全控制编写大量重
复代码的工作。
MAVEN配置POM
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
建立相关的包
在com.neu包中,建立类FirstApplication
在com.neu.ctrl包中,建立类HelloSecurityController
运行spring boot程序
观察控制台,可以看到,spring security框架给出一个临时密码,用于验证。
package com.neu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguratio
n;
@SpringBootApplication
public class FirstApplication {
public static void main(String[] args) {
SpringApplication.run(FirstApplication.class,args);
}
} p
ackage com.neu.ctrl;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/spring”)
public class HelloSecurityController {
@RequestMapping(“/security01”)
public String sayHello(){
return “Hello Spring Secuirty 安全管理框架”;
}
}
打开浏览器,开始访问spring security程序
当访问请求时,spring security框架开始进行AOP身份认证拦截,要求必须输入用户名与密码才可以登
录,默认的用户名为user(不区分大小写),密码是在控制台生成的。
登录失败提示:
spring security 深入细节
修改用户名与密码功能
在spring boot的配置文件中,可以指定与修改用户名与密码:
application.properties文件进行修改
输入正确的用户名和密码:
spring.security.user.name=jeflee
spring.security.user.password=123456
在项目中排除关闭权限验证
在spring boot的启动配置文件中,加入以下注解配置:
服务器重启后,再次访问项目,可以看到,已经排除了spring security功能
spring security配置文件指定验证用户列表
将第一个项目复制,产生一份新的spring security项目
package com.neu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguratio
n;
//@SpringBootApplication
//排除Secuirty的配置,让他不启用
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class FirstApplication {
public static void main(String[] args) {
SpringApplication.run(FirstApplication.class,args);
}
}
修改spring boot启动文件:FirstApplication
将application.properties文件中的内容注释掉,在配置文件中指定用户名与密码,不是spring security
指定的最佳配置方式 ,另外,通过继承类的方式来指定security列表。
修改HelloSecurityController
在项目中建立包com.neu.config
建立继承配置类文件MyWebSecurityConfig
package com.neu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguratio
n;
@SpringBootApplication
public class FirstApplication {
public static void main(String[] args) {
SpringApplication.run(FirstApplication.class,args);
}
} p
ackage com.neu.ctrl;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/spring”)
public class HelloSecurityController {
@RequestMapping(“/security02”)
public String sayHello(){
return “使用 Spring Secuirty 安全管理框架 配置列表 继承类的方式实现”;
}
} p
ackage com.neu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.authentication.builders.Authentic
ationManagerBuilder;
项目启动,测试结果:
输入继承类的验证列表中的用户名与密码,验证通过:
import
org.springframework.security.config.annotation.web.configuration.EnableWebSecuri
ty;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConf
igurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
//在方法中配置 用户和密码的信息, 作为登录的数据
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
PasswordEncoder pe = passwordEncoder();
auth.inMemoryAuthentication()
.withUser(“jeflee”)
.password(pe.encode(“123456”))
.roles();
auth.inMemoryAuthentication()
.withUser(“neu”)
.password(pe.encode(“123456”))
.roles();
auth.inMemoryAuthentication()
.withUser(“neuedu”)
.password(pe.encode(“neuedu”))
.roles();
} /
/创建密码的加密类
@Bean
public PasswordEncoder passwordEncoder(){
//创建PasawordEncoder的实现类, 实现类必须要求加密算法
return new BCryptPasswordEncoder();
}
}
列表中的多个密码进行多次测试,均通过AOP拦截案例验证,表示程序结果正常。
角色权限与身份认证
同一个用户可以有不同的角色,如果在用户层上,再加入角色层。权限系统会更加复杂。
同时,认证的精确度与粒度管理,可以细至方法级别。
开发角色权限
将spring_security_02项目进行复制,产生spring_security_03项目。
在此项目上,进行开发。
修改HelloController
配置MyWebSecurityConfig.java文件
package com.neu.ctrl;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloSecurityController {
@RequestMapping(“/spring”)
public String sayHello(){
return “使用配置文件的用户信息”;
} /
/指定 normal 和admin 角色都可以访问的方法
@RequestMapping(“/sec_user”)
@PreAuthorize(value = “hasAnyRole(‘admin’,’normal’)”)
public String helloCommonUser(){
return “配置有normal, admin角色的用户都可以访问这个方法”;
} /
/指定admin角色的访问方法
@RequestMapping(“/sec_admin”)
@PreAuthorize(“hasAnyRole(‘admin’)”)
public String helloAdmin(){
return “配置 admin角色的用户可以访问”;
}
}
package com.neu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.authentication.builders.Authentic
ationManagerBuilder;
import
org.springframework.security.config.annotation.method.configuration.EnableGlobal
MethodSecurity;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSecuri
ty;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConf
igurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/*
@EnableGlobalMethodSecurity:启用方法级别的认证
prePostEnabled:boolean 默认是false
true:表示可以使用@PreAuthorize注解 和 @PostAuthorize
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
//在方法中配置 用户和密码的信息, 作为登录的数据
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
PasswordEncoder pe = passwordEncoder();
auth.inMemoryAuthentication()
.withUser(“jeflee”)
.password(pe.encode(“123456”))
.roles(“normal”);
auth.inMemoryAuthentication()
.withUser(“neu”)
.password(pe.encode(“123456”))
.roles(“normal”);
auth.inMemoryAuthentication()
.withUser(“neuedu”)
.password(pe.encode(“neuedu”))
.roles(“admin”,”normal”);
} /
/创建密码的加密类
@Bean
public PasswordEncoder passwordEncoder(){
//创建PasawordEncoder的实现类, 实现类必须要求加密算法
return new BCryptPasswordEncoder();
}
}
测试项目
将项目启动,进行验证测试:
输入地址:localhost:8080/spring
这是对于方法级别的访问,只访问/spring 这个方法,输入用户名jeflee
由于jeflee用户具有normal角色,而
@RequestMapping(“/spring”) public String sayHello()
方法,并没有指定角色隔离,所以,此方法可以被访问到。
多次测试
访问如下方法:
//指定 normal 和admin 角色都可以访问的方法 @RequestMapping(“/sec_user”)
@PreAuthorize(value = “hasAnyRole(‘admin’,’normal’)”) public String helloCommonUser()
以上此方法设置了角色验证,只有具有admin和normal的两种角色的用户均可访问。
在配置文件中获取,jeflee,neu,neuedu,三个用户均具体以上角色权限。
均可以访问:
三次测试
再一次测试,访问以下方法:
//指定admin角色的访问方法 @RequestMapping(“/sec_admin”)
@PreAuthorize(“hasAnyRole(‘admin’)”) public String helloAdmin()
以上方法,需要具有admin角色权限的用户才可以访问。
此时,使用jeflee,与neu两个具有normal角色的用户进行访问,出现以下提示:
只有使用neuedu角色的用户访问,通过验证:
数据库中用户的安全验证
实际项目中,用户信息存储在关系型数据库中,用户与角色的案例验证做法如下:
通过JDBC从mysql数据库中,获取用户信息,进行安全验证。
设计思路
从数据库 mysql 中获取用户的身份信息(用户名称,密码,角色) 1)在 spring security 框架对象用
户信息的表示类是 UserDetails. UserDetails 是一个接口,高度抽象的用户信息类(相当于项目中的
User 类) User 类:是 UserDetails 接口的实现类, 构造方法有三个参数: username,password,
authorities 需要向 spring security 提供 User 对象, 这个对象的数据来自数据库的查询。 2)实现
UserDetailsService 接口, 重写方法 UserDetails loadUserByUsername(String var1) 在方法中获取数
据库中的用户信息, 也就是执行数据库的查询,条件是用户名称。
实现步骤
创建web模板的maven项目
修改配置文件pom.xml
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
编写配置文件/spring_security_04/src/main/resources/application.properties
在MYSQL数据库中,创建数据库springdb
spring.datasource.url=jdbc:mysql://localhost:3306/springdb
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.database=mysql
创建基于spring boot的启动
类/springsecurity_04/src/main/java/com/neu/springbootApplication.java
创建entity层,dao层和service层
entity层中的类:/spring_security_04/src/main/java/com/neu/entity/UserInfo.java
package com.neu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class springbootApplication {
public static void main(String[] args) {
// TODO Auto-generated method stub
SpringApplication.run(springbootApplication.class,args);
}
}
package com.neu.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
//表示当前类是一个实体类, 表示数据库中的一个表
//表名默认和类名一样的
@Entity
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
dao层中的类:/spring_security_04/src/main/java/com/neu/dao/UserInfoDao.java
//用户名称
private String username;
//密码
private String password;
//角色
private String role;
public Long getId() {
return id;
} p
ublic void setId(Long id) {
this.id = id;
} p
ublic String getUsername() {
return username;
} p
ublic void setUsername(String username) {
this.username = username;
} p
ublic String getPassword() {
return password;
} p
ublic void setPassword(String password) {
this.password = password;
} p
ublic String getRole() {
return role;
} p
ublic void setRole(String role) {
this.role = role;
}
} p
ackage com.neu.dao;
import com.neu.entity.UserInfo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserInfoDao extends JpaRepository
//按照username查询数据库信息
UserInfo findByUsername(String username);
}
service层中的类:
/spring_security_04/src/main/java/com/neu/service/UserInfoService.java
service层的实现
类:/spring_security_04/src/main/java/com/neu/service/impl/UserInfoServiceImpl.java
创建初始化数据库的工具类:/spring_security_04/src/main/java/com/neu/init/JdbcInit.java
package com.neu.service;
import com.neu.entity.UserInfo;
public interface UserInfoService {
UserInfo findUserInfo(String username);
} p
ackage com.neu.service.impl;
import com.neu.dao.UserInfoDao;
import com.neu.entity.UserInfo;
import com.neu.service.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserInfoServiceImpl implements UserInfoService {
@Autowired
private UserInfoDao dao;
@Override
public UserInfo findUserInfo(String username) {
UserInfo userinfo = dao.findByUsername(username);
return userinfo;
}
} p
ackage com.neu.init;
import com.neu.dao.UserInfoDao;
import com.neu.entity.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class JdbcInit {
@Autowired
启动spring boot 主应用程序,运行,看到有数据插入到数据库中:
private UserInfoDao dao;
@PostConstruct
public void init(){
PasswordEncoder encoder = new BCryptPasswordEncoder();
UserInfo u = new UserInfo();
u.setUsername(“jeflee”);
u.setPassword(encoder.encode(“123456”));
u.setRole(“normal”);
dao.save(u);
u = new UserInfo();
u.setUsername(“neuedu”);
u.setPassword(encoder.encode(“neuedu”));
u.setRole(“admin”);
dao.save(u);
}
}
至此,基础的数据工作准备完成。
下一步,实现userdetail接口,实现服务层。(UserDetailsService接口是Security框架中的接口)
创建包与类:/spring_security_04/src/main/java/com/neu/provider/MyUserDetailService.java
下一步,创建配置信息验证包与相关类:
package com.neu.provider;
import com.neu.dao.UserInfoDao;
import com.neu.entity.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 java.util.ArrayList;
import java.util.List;
@Component(“MyUserDetailService”)
public class MyUserDetailService implements UserDetailsService {
@Autowired
private UserInfoDao dao;
@Override
public UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException {
User user = null;
UserInfo userinfo = null;
if( username != null){
userinfo = dao.findByUsername(username);
if( userinfo != null){
List
//角色必须以ROLE
GrantedAuthority authority = new SimpleGrantedAuthority(“ROLE_”
+userinfo.getRole());
list.add(authority);
//创建User对象
user = new
User(userinfo.getUsername(),userinfo.getPassword(),list);
}
} r
eturn user;
}
}
/spring_security_04/src/main/java/com/neu/config/MyWebSecurityConfig.java
创建控制器层的包与类:/spring_security_04/src/main/java/com/neu/ctrl/HelloController.java
package com.neu.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.authentication.builders.Authentic
ationManagerBuilder;
import
org.springframework.security.config.annotation.method.configuration.EnableGlobal
MethodSecurity;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSecuri
ty;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConf
igurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier(“MyUserDetailService”)
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetailsService).passwordEncoder( new
BCryptPasswordEncoder());
}
} p
ackage com.neu.ctrl;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping(“/spring”)
public String sayHello(){
return “使用配置中的用户信息”;
//指定 normal 和admin 角色都可以访问的方法
@RequestMapping(“/sec_ser”)
@PreAuthorize(value = “hasAnyRole(‘admin’,’normal’)”)
public String helloCommonUser(){
return “验证权限有normal, admin角色的用户”;
} /
/指定admin角色的访问方法
@RequestMapping(“/sec_admin”)
@PreAuthorize(“hasAnyRole(‘admin’)”)
public String helloAdmin(){
return “验证权限只有 admin角色的用户可以访问”;
}
}
功能测试
对项目进行启动,测试:
有个问题,当再次启动SRPING BOOT时,注意看日志信息:
将两条记录删除,同时,将JdbcInit类中的文件注释掉。
多路径测试
再次重启spring boot,可以正常测试:
测试NORMAL方法,使用jeflee用户进行访问:localhost:8080/sec_user
用jeflee用户访问,sec_admin,可以看到,无权限进行访问。
而使用neuedu用户,则具有ADMIN的权限,可以访问。
认证和授权
认证表示是不是系统认为合法的用户。
授权表示作为一个合法的用户,能在系统中使用什么样的功能。
RBAC模型
RBAC 是基于角色的访问控制(Role-Based Access Control ) 在 RBAC 中,权限与角色相关联,用户
通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相
互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
其基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立
一个角色集合。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后,该用户就拥有此角
色的所有操作权限。
这样做的好处是,不必在每次创建用户时都进行分配权限的操作,只要分配用户相应的角色即可,而且
角色的权限变更比用户的权限变更要少得多,这样将简化用户的权限管理, 减少系统的开销。
RBAC: 用户是属于角色的, 角色拥有权限的集合。 用户属于某个角色, 他就具有角色对应的权限。
RBAC 流程图
举例说明RBAC工作原理
系统中有张三,李四,他们是普通员工,只能查看数据。
系统中经理,副经理他们能修改数据。
设计有权限的集合,角色: 经理角色,具有修改数据的权限,删除, 查看等等。
普通用户角色: 只读角色,只能看数据 ,不能修改,删除。
让张三,李四是只读的,普通用户角色。
让经理,副经理他们都是 经理角色。
公司以后增加新的普通员工,加入到“普通用户角色”就可以了,不 需要在增加新的角色。
公司增加经理了, 只要加入到“经理角色”就可以了。
权限:能对资源的操作, 比如增加,修改,删除,查看等等。
角色:自定义的, 表示权限的集合。一个角色可以有多个权限。
RBAC表设计
RBAC 设计中的表设计:
1. 用户表: 用户认证(登录用到的表) 用户名,密码,是否启用,是否锁定等信息。
2. 角色表:定义角色信息角色名称, 角色的描述。
3. 用户和角色的关系表: 用户和角色是多对多的关系。 一个用户可以有多个角色, 一个角色可以有
多个用户。
4. 权限表, 角色和权限的关系表 角色可以有哪些权限。
RBAC spring Security实践
spring specurity 中认证的接口和类
1) UserDetails:接口,表示用户信息的。 boolean isAccountNonExpired(); 账号是否过期 boolean
isAccountNonLocked();账号是否锁定 boolean isCredentialsNonExpired();证书是否过期 boolean
isEnabled();账号是否启用 Collection<? extends GrantedAuthority> getAuthorities(); 权限集合User
实现类 org.springframework.security.core.userdetails.User 可以:自定义类实现 UserDetails 接口,
作为你的系统中的用户类。这 个类可以交给 spring security 使用。
2) UserDetailsService 接口: 主要作用:获取用户信息,得到是 UserDetails 对象。一般项目中都需
要自定义类实现这个接口,从数据库中获取数据。一个方法需要实现: UserDetails
loadUserByUsername(String var1) :根据用户名称,获取用 户信息(用户名称,密码,角色结合,
是否可用,是否锁定等信息)
UserDetailsService 接口的实现类:
JdbcUserDetailsManager :用户信息存放在数据库中,底层使用 jdbcTemplate 操作数据库。 可以
JdbcUserDetailsManager 中的方法完 成用户的管理 createUser : 创建用户 updateUser:更新用户
deleteUser:删除用户 userExists:判断用户是否存在
RBAC设计原则 :独立反复使用,与系统解偶合。
开发RBAC
使用用户和角色,实现认证用户
过程步骤:
1.新建maven项目 2.加入gav坐标 1)spring-boot : 2.0.6 2)spring-security 3)spring-web 4)
spring和mybatis相关的依赖 5)mysql驱动 3.编写application.properties 连接数据库,创建连接池
4.创建自己的user类,代替UserDetatils
5.创建自定义的UserDetatilsService实现类 在重写方法中,查询数据库获取用户信息, 获取角色数
据。 构建UserDetatils实现类对象。
6.创建类继承WebSecurityConfigurerAdapter 自定义安全的配置
7.自定义登录 1)传统form登录 2)ajax登录
8.创建Controller
9.测试运行
RBAC在security的最佳实践
建立mysql数据库中的相关表
定义用户(tuser),角色(t_role),角色关系表(t_user_role)
创建普通maven工程
create database rbac;
create table t_user(id int primary key auto_increment,
username varchar(200),password varchar(200),realname
varchar(200),isexpired int,isenable int,islock int,iscredentials int, createtime
date,logintime date);
create table t_role(id int primary key auto_increment,rolename
varchar(200),rolememo varchar(200));
create table t_user_role(userid int,roleid int);
//isenable 账号是否可用
//islock 账号是否锁定
//iscredentials 账号是否凭证可用
配置maven pom.xml文件
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
创建资源文件application.properties
/spring_security_05/src/main/resources/application.properties
在resources目录中,创建静态页面文件夹static,
并创建静态页面
/spring_security_05/src/main/resources/static/index.html
/spring_security_05/src/main/resources/static/mylogin.html
在resources目录中,创建mapper目录,并创建配置文件
/spring_security_05/src/main/resources/mapper/SysRoleMapper.xml
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/rbac
spring.datasource.username=root
spring.datasource.password=root
mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
mybatis.type-aliases-package=com.neu.entity
<!DOCTYPE html>
验证RBAC
验证user
验证read
验证admin
退出系统
<!DOCTYPE html>
自定义登录页面
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE mapper
PUBLIC “-//mybatis.org//DTD Mapper 3.0//EN”
“http://mybatis.org/dtd/mybatis-3-mapper.dtd">
/spring_security_05/src/main/resources/mapper/SysUserMapper.xml
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE mapper
PUBLIC “-//mybatis.org//DTD Mapper 3.0//EN”
“http://mybatis.org/dtd/mybatis-3-mapper.dtd">
insert into t_user(username,password,realname,isexpired,
isenable,islock,iscredentials,createtime,logintime)
values(#{username},#{password},#{realname},#{isExpired},#{isEnabled},
#{isLocked},#{isCredentials},#{createTime},#{loginTime})
创建实体类:/spring_security_05/src/main/java/com/neu/entity/SysUser.java
package com.neu.entity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Date;
import java.util.List;
public class SysUser implements UserDetails {
private Integer id;
private String username;
private String password;
private String realname;
private boolean isExpired;
private boolean isLocked;
private boolean isCredentials;
private boolean isEnabled;
private Date createTime;
private Date loginTime;
private List
public SysUser() {
} p
ublic SysUser(String username, String password, String realname,
boolean isExpired, boolean isLocked,
boolean isCredentials, boolean isEnabled,
Date createTime, Date loginTime,List
authorities) {
this.username = username;
this.password = password;
this.realname = realname;
this.isExpired = isExpired;
this.isLocked = isLocked;
this.isCredentials = isCredentials;
this.isEnabled = isEnabled;
this.createTime = createTime;
this.loginTime = loginTime;
this.authorities = authorities;
} @
Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
} @
Override
public String getPassword() {
return password;
} @
Override
public String getUsername() {
return username;
} @
Override
public boolean isAccountNonExpired() {
return isExpired;
} @
Override
public boolean isAccountNonLocked() {
return isLocked;
} @
Override
public boolean isCredentialsNonExpired() {
return isCredentials;
} @
Override
public boolean isEnabled() {
return isEnabled;
} p
ublic Integer getId() {
return id;
} p
ublic Date getCreateTime() {
return createTime;
} p
ublic Date getLoginTime() {
return loginTime;
} p
ublic String getRealname() {
return realname;
} p
ublic void setId(Integer id) {
this.id = id;
} p
ublic void setUsername(String username) {
this.username = username;
} p
ublic void setPassword(String password) {
this.password = password;
} p
ublic void setRealname(String realname) {
this.realname = realname;
/spring_security_05/src/main/java/com/neu/entity/SysRole.java
} p
ublic void setExpired(boolean expired) {
isExpired = expired;
} p
ublic void setLocked(boolean locked) {
isLocked = locked;
} p
ublic void setCredentials(boolean credentials) {
isCredentials = credentials;
} p
ublic void setEnabled(boolean enabled) {
isEnabled = enabled;
} p
ublic void setCreateTime(Date createTime) {
this.createTime = createTime;
} p
ublic void setLoginTime(Date loginTime) {
this.loginTime = loginTime;
} p
ublic void setAuthorities(List
this.authorities = authorities;
} @
Override
public String toString() {
return “SysUser{“ +
“id=” + id +
“, username=’” + username + ‘\’’ +
“, password=’” + password + ‘\’’ +
“, realname=’” + realname + ‘\’’ +
“, isExpired=” + isExpired +
“, isLocked=” + isLocked +
“, isCredentials=” + isCredentials +
“, isEnabled=” + isEnabled +
“, createTime=” + createTime +
“, loginTime=” + loginTime +
“, authorities=” + authorities +
‘}’;
}
} p
ackage com.neu.entity;
public class SysRole {
private Integer id;
private String name;
private String memo;
创建mapper
/spring_security_05/src/main/java/com/neu/mapper/SysRoleMapper.java
/spring_security_05/src/main/java/com/neu/mapper/SysUserMapper.java
public Integer getId() {
return id;
} p
ublic void setId(Integer id) {
this.id = id;
} p
ublic String getName() {
return name;
} p
ublic void setName(String name) {
this.name = name;
} p
ublic String getMemo() {
return memo;
} p
ublic void setMemo(String memo) {
this.memo = memo;
} @
Override
public String toString() {
return “SysRole{“ +
“id=” + id +
“, name=’” + name + ‘\’’ +
“, memo=’” + memo + ‘\’’ +
‘}’;
}
} p
ackage com.neu.mapper;
import com.neu.entity.SysRole;
import java.util.List;
public interface SysRoleMapper {
List
} p
ackage com.neu.mapper;
import com.neu.entity.SysUser;
创建程序启动类
/spring_security_05/src/main/java/com/neu/springApplication.java
import org.springframework.stereotype.Repository;
//@Repository :创建dao对象
@Repository
public interface SysUserMapper {
int insertSysUser(SysUser user);
//根据账号名称,获取用户信息
SysUser selectSysUser(String username);
} p
ackage com.neu;
import com.neu.entity.SysUser;
import com.neu.mapper.SysUserMapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@MapperScan(value = “com.neu.mapper”)
@SpringBootApplication
public class springApplication {
@Autowired
SysUserMapper userMapper;
public static void main(String[] args) {
SpringApplication.run(springApplication.class,args);
} /
/@PostConstruct
public void jdbcInit(){
Date curDate = new Date();
PasswordEncoder encoder = new BCryptPasswordEncoder();
List
//参数角色名称,需要以”ROLE
创建spring security 配置文件类
/springsecurity_05/src/main/java/com/neu/config/CustomSecurityConfig.java
GrantedAuthority authority = new
SimpleGrantedAuthority(“ROLE“+”READ”);
list.add(authority);
SysUser user = new SysUser(
“l4”,encoder.encode(“123456”),”l4”,true,true,true,true,curDate, curDate, list
);
userMapper.insertSysUser(user);
List
GrantedAuthority authority2 = new
SimpleGrantedAuthority(“ROLE“+”AMDIN”);
GrantedAuthority authority3 = new
SimpleGrantedAuthority(“ROLE“+”USER”);
list.add(authority2);
list.add(authority3);
SysUser user2 = new SysUser(
“neuedu”,encoder.encode(“neuedu”),”admin”,true,true,true,true,curDate, curDate,
list2
);
userMapper.insertSysUser(user2);
}
}
package com.neu.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.authentication.builders.Authentic
ationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSecuri
ty;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConf
igurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@EnableWebSecurity
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
创建service层相关类
/springsecurity_05/src/main/java/com/neu/service/JdbcUserDetatilsService.java
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
//super.configure(auth);
auth.userDetailsService(userDetailsService).passwordEncoder(new
BCryptPasswordEncoder());
} @
Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println(“**configure HttpSecurity**“);
http.authorizeRequests()
.antMatchers(“/index”).permitAll()
.antMatchers(“/access/user/“).hasRole(“USER”)
.antMatchers(“/access/read/“).hasRole(“READ”)
.antMatchers(“/access/admin/**”).hasRole(“ADMIN”)
.anyRequest().authenticated()
.and()
.formLogin();
}
} p
ackage com.neu.service;
import com.neu.entity.SysRole;
import com.neu.entity.SysUser;
import com.neu.mapper.SysRoleMapper;
import com.neu.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class JdbcUserDetatilsService implements UserDetailsService {
@Autowired
private SysUserMapper userMapper;
@Autowired
private SysRoleMapper roleMapper;
创建控制器层与相关的类:
/spring_security_05/src/main/java/com/neu/ctrl/IndexController.java
/spring_security_05/src/main/java/com/neu/ctrl/MyController.java
@Override
public UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException {
//1. 根据username 获取SysUser
SysUser user = userMapper.selectSysUser(username);
System.out.println(“loadUserByUsername user:”+user);
if( user != null){
//2. 根据userid的,获取role
List
System.out.println(“roleList:”+ roleList);
List
String roleName = “”;
for (SysRole role : roleList) {
roleName = role.getName();
GrantedAuthority authority = new
SimpleGrantedAuthority(“ROLE
authorities.add(authority);
} u
ser.setAuthorities(authorities);
return user;
} r
eturn user;
}
} p
ackage com.neu.ctrl;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class IndexController {
@GetMapping(“/index”)
public String toIndexHtml(){
return “forward:/index.html”;
}
} p
ackage com.neu.ctrl;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@GetMapping(value = “/access/user”,produces = “text/html;charset=utf-8”)
public String sayUser(){
return “you 是 user 角色”;
} @
GetMapping(value = “/access/read”,produces = “text/html;charset=utf-8”)
public String sayRead(){
return “neuedu 有 read 角色”;
} @
GetMapping(value = “/access/admin”,produces = “text/html;charset=utf-8”)
public String sayAdmin(){
return “you 是 user , admin 角色”;
}
}
发布测试
运行spring boot程序,进行测试



使用l4用户与neuedu用户登录,经过验证通过。
如果是数据库权限外的非法用户,则提示:
向RABC数据库中的t_role表中加入以下信息,此内容与启动类的集合内容一致。
向t_user_role表中加入数据
mysql> insert into t_role(rolename,rolememo) values (‘USER’,’USER’);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t_role(rolename,rolememo) values (‘ADMIN’,’ADMIN’);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t_role(rolename,rolememo) values (‘READ’,’READ’);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t_user_role values(1,1);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t_user_role values(1,2);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t_user_role values(2,1);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t_user_role values(2,2);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t_user_role values(2,3);
Query OK, 1 row affected (0.01 sec)
l4 有1,2两个权限,neuedu有1,2,3三个权限。
测试角色表,角色权限表
使用l4用户测试,打开地址:
看到登录页面,使用l4进行登录后,进行角色授权,呈现结果。
再次使用l4用户,测试验证read角色。
清除缓存,使用neuedu用户,测试admin角色权限
最后,重启程序,清除缓存,使用neuedu用户重新登录。
因为neuedu用户有最高权限,所以,所有请求均可以访问。
结论:只有经过正确用户名和密码,经过授权,才可登录系统。
同时,只有经过角色与权限的认证,具备权限,才可以访问指定的资源。
认证页面
以上所有功能,登录页面都是系统提供的。
spring security系统默认的登录地址:http://lolcalhost:8080/login
请求方式为:post
是通过过滤器来起作用的。
使用/spring_security_05/src/main/resources/static/mylogin.html 来作为自定义的登录页面。
mylogin.html 写完之后,框架并不知道这个页面,需要让框架知道。
修改以下内容:
访问首页:http://localhost:8080/index
解决办法:修改以下代码
重新启动,测试运行:
在http://localhost:8080/index 点击验证USER.
http.authorizeRequests()
.antMatchers(“/index”,”/mylogin.html”,”/login”,”/error.html”).permitAll()
.antMatchers(“/access/user/“).hasRole(“USER”)
.antMatchers(“/access/read/“).hasRole(“READ”)
.antMatchers(“/access/admin/**”).hasRole(“ADMIN”)
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage(“/mylogin.html”)
.loginProcessingUrl(“/login”)
.failureUrl(“/error.html”)
.and().csrf().disable();
看到了自定义页面,输入neuedu用户,正确密码,看到请求返回值,错误流程进入error.
html页面。
错误情况 :
总结
Spring Security是基于Spring提供声明式安全保护的安全性框架。Spring Security提供了完整的安全性
解决方案,能够在Web请求级别和方法调用级别处理身份认证和授权。
Spring Security从两个角度来解决安全性问题:
1. 使用Servlet规范中的Filter保护Web请求并限制URL级别的访问;
2. 使用Spring AOP保护方法调用——借助于动态代理和使用通知,确保只有具备适当权限的用户才
能访问安全保护的方法。