Spring Security


1、认证和授权

关于认证和授权,查资料是这样解释的

认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。

授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。

先认证后授权

2、权限模块数据模型

要实现最终的权限控制,需要有一套数据模型来做支撑,一般的表结构如下:

用户表——权限表——角色表——菜单表——用户角色关系表——角色权限关系表——角色菜单关系表

类似上面这种表结构

认证的过程:只需要用户表,在登录时查询用户信息,判断用户名和密码是否正确。这一过程较为简单

授权的过程:用户必须完成认证之后才可以进行授权,首先可以根据用户查询其角色,再根据角色查询对应的菜单,这样就确定了用户能够看到哪些菜单。然后再根据用户的角色查询对应的权限,这样就确定了用户拥有哪些权限。所以授权过程会用到上面7张表。

3、Spring Security

SS是spring提供的安全认证服务框架。使用ss可以帮助我们简化认证和授权的过程。

这是他的官网:https://docs.spring.io/spring-security/site/docs/5.4.5/reference/html5/


    org.springframework.security
    spring-security-web
    ${spring.security.version}


    org.springframework.security
    spring-security-config
    ${spring.security.version}

3.1、工程搭建

创建maven工程,打包方式为war包,把需要的依赖文件添加到pom.xml文件中。

如果需要tomcat插件,需进行以下配置:


        
            
                org.apache.tomcat.maven
                tomcat7-maven-plugin
                
                    
                    85
                    
                    /
                
            
        
    

提供index.html文件,提供内容Hello Spring Security!

3.2、配置web.xml

在web.xml中配置SpringMVC中的DisPatcherServlet和用于整合第三方框架的DelegatingFilterProxy,用于整合spring Security。


  Archetype Created Web Application
  
    
    springSecurityFilterChain
    org.springframework.web.filter.DelegatingFilterProxy
  
  
    springSecurityFilterChain
    /*
  
  
    springmvc
    org.springframework.web.servlet.DispatcherServlet
    
    
      contextConfigLocation
      classpath:spring-security.xml
    
    1
  
  
    springmvc
    *.do
  

?

注意:DelegatingFilterProxy用于整合第三方框架 整合Spring Security时过滤器的名称必须为springSecurityFilterChain,否则会抛出NoSuchBeanDefinitionException异常

3.3 配置 spring-security.xml

在spring-security.xml中主要配置认证规则和拦截管理器。

<?xml version="1.0" encoding="UTF-8"?>

    
    

        
    
    
        
            

                
            
        
    
?

3.3 运行测试

访问localhost:85/index.html

因为没有权限,所以跳转到spring security自带的登录页面。

进行登录后,重新访问

访问成功了!

4、对入门案例进行改进

前面我们已经完成了Spring Security的入门案例,通过入门案例我们可以看到,Spring Security将我们项目中的所有资源都保护了起来,要访问这些资源必须要完成认证而且需 要具有ROLE_ADMIN角色。

但是入门案例中的使用方法离我们真实生产环境还差很远,还存在如下一些问题:

1、项目中我们将所有的资源(所有请求URL)都保护起来,实际环境下往往有一些资源 不需要认证也可以访问,也就是可以匿名访问。 access="hasRole('ROLE_ADMIN')" />

2、登录页面是由框架生成的,而我们的项目往往会使用自己的登录页面。

3、直接将用户名和密码配置在了配置文件中,而真实生产环境下的用户名和密码往往保存在数据库中。

4、在配置文件中配置的密码使用明文,这非常不安全,而真实生产环境下密码需要进行 加密。

4.1配置可匿名访问的资源

因为上面的配置中“/**”代表对所有的资源都进行权限的判断,但实际生产环境中,我们并不需要将所有的页面都进行权限配置比如登录页面。

所以我们需要进行下面的配置:

    
    

然后再运行测试,发现a.html和b.html可以访问,之前的index.html任然需要登录才可以进入。

4.2 配置自己自定义的登录页面

准备工作:一个自定义的登录页面,放入相应的目录中

  1. 配置该页面可匿名访问

  1. 如果我们使用自己的登录页面,必须配置登录表单,页面提交的登录请求由框架处理

login-page="/pages/login.html":表单页面

username-parameter="username" password-parameter="password" :相对应的用户名和密码

default-target-url="/index.html" authentication-failure-url="/pages/login.html" :登录成功和失败所对应的页面

  1. 关闭CsrfFilter过滤器,默认开启


为什么关闭csrs过滤器,因为在使用默认的登录界面,会有一个隐藏域生成一个随机值,框架进行对比,如果不一致,会认为这是伪造的认证请求,登录操作会被禁用。

4.3从数据库查询信息(首先通过map模拟数据库)

如果我们要从数据库动态查询用户信息,就必须按照spring security框架的要求提供一个 实现UserService接口的实现类,并按照框架的要求进行配置即可。框架会自动调用实现类中的方法并自动进行密码校验。

实现代码:

先通过map集合模拟数据库查询

public class SpringSercuity implements UserDetailsService {
?
    public static Map map=new HashMap<>();
    static {
        User user=new User();
        user.setUsername("xiaohong");
        user.setPassword("123456");
        User user1=new User();
        user1.setUsername("xiaoming");
        user1.setPassword("123456");
        map.put(user.getUsername(),user);
        map.put(user1.getUsername(),user1);
    }
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //System.out.println(username);
        User user=map.get(username);
        //System.out.println(user.toString());
        if (user == null){
            return null;
        }else {
            //当前登录用户授权,后期改为数据库查询当前对应的权限
            List list =new ArrayList<>();
            list.add(new SimpleGrantedAuthority("permission_A")); //授予权限
            list.add(new SimpleGrantedAuthority("permission_A"));
            list.add(new SimpleGrantedAuthority("ROLE_ADMIN")); //授予角色
            org.springframework.security.core.userdetails.User SecurityUser=new org.springframework.security.core.userdetails.User(username,"{noop}"+user.getPassword(),list);
            return SecurityUser;
        }
?
    }
}

4.4 对密码进行加密

前面我们使用的密码都是明文的,这是非常不安全的。一般情况下用户的密码需要进行 加密后再保存到数据库中。

常见的密码加密方式有: 3DES、AES、DES:使用对称加密算法,可以通过解密来还原出原始密码

MD5、SHA1:使用单向HASH算法,无法通过计算还原出原始密码,但是可以建立彩虹表进行查表破解

bcrypt:将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而 无需单独处理salt问题 加密后的格式一般为:

$2a$10$/bTVvqqlH9UiE0ZJZ7N2Me3RIgUCdgMheyTgV0B4cMCSokPa.6oCa

加密后字符串的长度为固定的60位。其中:$是分割符,无意义;2a是bcrypt加密版本 号;10是cost的值;而后的前22位是salt值;再然后的字符串就是密码的密文了。

实现步骤:

  1. 配置文件中添加相关的加密对象的bean


    
  1. 自动注入

@Autowired
    private BCryptPasswordEncoder passwordEncoder;
  1. 调用自带方法,实现密码加密

User user=new User();
        user.setUsername("xiaohong");
        //使用BCrypt对密码进行加密
        user.setPassword(passwordEncoder.encode("admin"));
        User user1=new User();
        user1.setUsername("xiaoming");
        user1.setPassword(passwordEncoder.encode("admin"));

4.5 配置多种校验规则

为了测试方便,首先在项目中创建a.html、b.html、c.html、d.html几个页面 修改spring-security.xml文件:









记得把上面案例用到的校验规则移除

access="hasRole('ROLE_ADMIN')==access="hasRole('ADMIN')":框架会自动加入ROLE_

4.6 注解方式权限控制

Spring Security除了可以在配置文件中配置权限校验规则,还可以使用注解方式控制类 中方法的调用。

例如Controller中的某个方法要求必须具有某个权限才可以访问,此时就可以使用Spring Security框架提供的注解方式进行控制。

实现步骤:

第一步:在spring-security.xml文件中配置组件扫描,用于扫描Controller

  

第二步:在spring-security.xml文件中开启权限注解支持

 

第三步:创建Controller类并在Controller的方法上加入注解进行权限控制

@RestController 
@RequestMapping("/hello") public class HelloController { 
    @RequestMapping("/add") 
    @PreAuthorize("hasAuthority('add')")//表示用户必须拥有add权限才能调用当前方法
    public String add(){ 
        System.out.println("add..."); 
        return "success"; 
    }
    @RequestMapping("/delete") 
    @PreAuthorize("hasRole('ROLE_ADMIN')")//表示用户必须拥有ROLE_ADMIN角色 才能调用当前方法 
    public String delete(){ 
        System.out.println("delete..."); 
        return "success"; 
    } 
}

4.7 退出登录

用户完成登录后Spring Security框架会记录当前用户认证状态为已认证状态,即表示用 户登录成功了。那用户如何退出登录呢?

我们可以在spring-security.xml文件中进行如下配置:

 

通过上面的配置可以发现,如果用户要退出登录,只需要请求/logout.do这个URL地址就 可以,同时会将当前session失效,最后页面会跳转到login.html页面。