Spring Security
    认证的概念
    进入移动互联网时代,每天都在刷手机,常用的软件有微信、支付宝、头条等,拿微信来举例子说明认
    证 相关的基本概念,在初次使用微信前需要注册成为微信用户,然后输入账号和密码即可登录微信,输
    入账号和密码 登录微信的过程就是认证。
    系统为什么要认证? 认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。
    认证 :用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的
    身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登
    录,二维码登录,手 机短信登录,指纹认证等方式。
    用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为
    了保持当前 用户的登录状态所提供的机制,常见的有基于session方式、基于token方式等。
    基于session的认证方式
    它的交互流程是,用户认证成功后,在服务端生成用户相关的数据保存在session(当前会话)中,发给客
    户端的 sesssion_id 存放到 cookie 中,这样用户客户端请求时带上 session_id 就可以验证服务器端是
    否存在 session 数 据,以此完成用户的合法校验,当用户退出系统或session过期销毁时,客户端的
    session_id也就无效。
    image.png
    基于token方式
    交互流程是,用户认证成功后,服务端生成一个token发给客户端,客户端可以放到 cookie 或
    localStorage 等存储中,每次请求时带上 token,服务端收到token通过验证后即可确认用户身份
    基于session的认证方式由Servlet规范定制,服务端要存储session信息需要占用内存资源,客户端需要
    支持 cookie;基于token的方式则一般不需要服务端存储token,并且不限制客户端的存储方式。如今
    移动互联网时代 更多类型的客户端需要接入系统,系统多是采用前后端分离的架构进行实现,所以基于
    token的方式更适合。
    image.png
    Spring Security 快速上手
    Spring Security介绍
    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框
    架。由于它 是Spring生态系统中的一员,因此它伴随着整个Spring生态系统不断修正、升级,在spring
    boot项目中加入spring security十分方便,使用Spring Security 减少了为企业系统安全控制编写大量重
    复代码的工作。
    image.png
    image.png
    MAVEN配置POM
    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">
    4.0.0
    com.neu
    spring_security_01
    建立相关的包
    image.png
    0.0.1-SNAPSHOT


    org.springframework.boot
    spring-boot-parent
    2.0.6.RELEASE





    org.springframework.boot
    spring-boot-starter-web



    org.springframework.boot
    spring-boot-starter-security






    org.springframework.boot
    spring-boot-maven-plugin




    在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程序
    image.png
    当访问请求时,spring security框架开始进行AOP身份认证拦截,要求必须输入用户名与密码才可以登
    录,默认的用户名为user(不区分大小写),密码是在控制台生成的。
    image.png
    登录失败提示:
    image.png
    spring security 深入细节
    修改用户名与密码功能
    在spring boot的配置文件中,可以指定与修改用户名与密码:
    application.properties文件进行修改
    输入正确的用户名和密码:
    image.png
    spring.security.user.name=jeflee
    spring.security.user.password=123456
    在项目中排除关闭权限验证
    在spring boot的启动配置文件中,加入以下注解配置:
    服务器重启后,再次访问项目,可以看到,已经排除了spring security功能
    image.png
    spring security配置文件指定验证用户列表
    将第一个项目复制,产生一份新的spring security项目
    image.png
    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;
    项目启动,测试结果:
    image.png
    输入继承类的验证列表中的用户名与密码,验证通过:
    image.png
    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()
    方法,并没有指定角色隔离,所以,此方法可以被访问到。
    image.png
    多次测试
    访问如下方法:
    //指定 normal 和admin 角色都可以访问的方法 @RequestMapping(“/sec_user”)
    @PreAuthorize(value = “hasAnyRole(‘admin’,’normal’)”) public String helloCommonUser()
    以上此方法设置了角色验证,只有具有admin和normal的两种角色的用户均可访问。
    在配置文件中获取,jeflee,neu,neuedu,三个用户均具体以上角色权限。
    均可以访问:
    image.png
    image.png
    三次测试
    再一次测试,访问以下方法:
    //指定admin角色的访问方法 @RequestMapping(“/sec_admin”)
    @PreAuthorize(“hasAnyRole(‘admin’)”) public String helloAdmin()
    以上方法,需要具有admin角色权限的用户才可以访问。
    此时,使用jeflee,与neu两个具有normal角色的用户进行访问,出现以下提示:
    image.png
    只有使用neuedu角色的用户访问,通过验证:
    image.png
    数据库中用户的安全验证
    实际项目中,用户信息存储在关系型数据库中,用户与角色的案例验证做法如下:
    通过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项目
    image.png
    image.png
    image.png
    修改配置文件pom.xml
    xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
    xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/maven-v4_0_0.xsd">
    4.0.0
    com.neu
    spring_security_04
    war
    0.0.1-SNAPSHOT
    spring_security_04 Maven Webapp
    http://maven.apache.org

    UTF-8
    1.7
    1.7



    org.springframework.boot
    spring-boot-parent
    2.0.6.RELEASE




    org.springframework.boot
    spring-boot-starter-web



    org.springframework.boot
    spring-boot-starter-security



    mysql
    mysql-connector-java
    5.1.9



    org.springframework.boot
    spring-boot-starter-data-jpa





    org.springframework.boot
    spring-boot-maven-plugin



    编写配置文件/spring_security_04/src/main/resources/application.properties
    在MYSQL数据库中,创建数据库springdb
    image.png

    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 主应用程序,运行,看到有数据插入到数据库中:
    image.png
    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 list = new ArrayList<>();
    //角色必须以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时,注意看日志信息:
    image.png
    将两条记录删除,同时,将JdbcInit类中的文件注释掉。
    image.png

    多路径测试
    再次重启spring boot,可以正常测试:
    image.png
    image.png
    测试NORMAL方法,使用jeflee用户进行访问:localhost:8080/sec_user
    用jeflee用户访问,sec_admin,可以看到,无权限进行访问。
    而使用neuedu用户,则具有ADMIN的权限,可以访问。
    认证和授权
    认证表示是不是系统认为合法的用户。
    授权表示作为一个合法的用户,能在系统中使用什么样的功能。
    RBAC模型
    RBAC 是基于角色的访问控制(Role-Based Access Control ) 在 RBAC 中,权限与角色相关联,用户
    通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相
    互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
    其基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立
    一个角色集合。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后,该用户就拥有此角
    色的所有操作权限。
    这样做的好处是,不必在每次创建用户时都进行分配权限的操作,只要分配用户相应的角色即可,而且
    角色的权限变更比用户的权限变更要少得多,这样将简化用户的权限管理, 减少系统的开销。
    RBAC: 用户是属于角色的, 角色拥有权限的集合。 用户属于某个角色, 他就具有角色对应的权限。
    RBAC 流程图
    image.png
    举例说明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工程
    image.png
    image.png
    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文件
    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">
    4.0.0
    com.neu
    spring_security_05
    0.0.1-SNAPSHOT
    创建资源文件application.properties
    /spring_security_05/src/main/resources/application.properties

    UTF-8
    1.7
    1.7



    org.springframework.boot
    spring-boot-parent
    2.0.6.RELEASE





    org.springframework.boot
    spring-boot-starter-web



    org.springframework.boot
    spring-boot-starter-security



    mysql
    mysql-connector-java
    5.1.9



    org.mybatis.spring.boot
    mybatis-spring-boot-starter
    2.0.1





    org.springframework.boot
    spring-boot-maven-plugin




    在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 authorities;
    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 authorities) {
    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 selectRoleByUser(Integer userId);
    } 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 list = new ArrayList<>();
    //参数角色名称,需要以”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 list2 = new ArrayList<>();
    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 roleList = roleMapper.selectRoleByUser(user.getId());
    System.out.println(“roleList:”+ roleList);
    List authorities = new ArrayList<>();
    String roleName = “”;
    for (SysRole role : roleList) {
    roleName = role.getName();
    GrantedAuthority authority = new
    SimpleGrantedAuthority(“ROLE
    “+roleName);
    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程序,进行测试
    image.png
    image.png
    image.png

    使用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三个权限。
    image.png
    测试角色表,角色权限表
    使用l4用户测试,打开地址:
    image.png

    看到登录页面,使用l4进行登录后,进行角色授权,呈现结果。
    再次使用l4用户,测试验证read角色。
    清除缓存,使用neuedu用户,测试admin角色权限
    image.png
    最后,重启程序,清除缓存,使用neuedu用户重新登录。
    因为neuedu用户有最高权限,所以,所有请求均可以访问。
    结论:只有经过正确用户名和密码,经过授权,才可登录系统。
    同时,只有经过角色与权限的认证,具备权限,才可以访问指定的资源。
    认证页面
    以上所有功能,登录页面都是系统提供的。
    spring security系统默认的登录地址:http://lolcalhost:8080/login
    image.png
    请求方式为:post
    是通过过滤器来起作用的。
    使用/spring_security_05/src/main/resources/static/mylogin.html 来作为自定义的登录页面。
    mylogin.html 写完之后,框架并不知道这个页面,需要让框架知道。
    修改以下内容:
    image.png

    访问首页:http://localhost:8080/index
    image.png
    解决办法:修改以下代码
    重新启动,测试运行:
    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页面。
    image.png
    错误情况 :
    image.png
    总结
    Spring Security是基于Spring提供声明式安全保护的安全性框架。Spring Security提供了完整的安全性
    解决方案,能够在Web请求级别和方法调用级别处理身份认证和授权。
    Spring Security从两个角度来解决安全性问题:
    1. 使用Servlet规范中的Filter保护Web请求并限制URL级别的访问;
    2. 使用Spring AOP保护方法调用——借助于动态代理和使用通知,确保只有具备适当权限的用户才
    能访问安全保护的方法。