springboot依赖



<!--spring security-->
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
</dependency>




简介



Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。正如你可能知道的关于安全方面的两个主要区域是“认证”和“授权”(或者访问控制),一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两点也是 Spring Security 重要核心功能。



(1)用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录



(2)用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情。



特点

1、和spring无缝整合

2、全面的权限控制

3、转为Web开发而设计

      1)旧版本不能脱离Web环境使用

       2)新版本对整个框架进行了分层抽取,分成了核心模块和 Web 模块。单独引入核心模块就可以脱离 Web 环境

4、重量级

与shiro对比shiro的特点

1、轻量级

2、通用性

      1)好处:不限于Web环境,可以脱离Web环境使用

      2)缺陷:在Web环境下一些特定的需求需要手动编写代码实现

spring security入门

新建springboot工程或moudle,添加web和security依赖,写一个Controller,快速测试一下,会在启动目录中看到随机生成的密码,用户名默认为user

spring跳过登录认证 基于spring的用户登录验证_前端

基本原理

本质就是一个过滤链

从项目的启动日志就可以看到

spring跳过登录认证 基于spring的用户登录验证_安全_02

来看三个过滤器的源码



FilterSecurityInterceptor:是一个方法级的权限过滤器, 基本位于过滤链的最底部



spring跳过登录认证 基于spring的用户登录验证_安全_03

 可以看到就是一个过滤器

spring跳过登录认证 基于spring的用户登录验证_安全_04

 核心验证放行

spring跳过登录认证 基于spring的用户登录验证_前端_05



super.beforeInvocation(fi) 表示查看之前的 filter 是否通过。



fi.getChain().doFilter(fi.getRequest(), fi.getResponse());表示真正的调用后台的服务。



ExceptionTranslationFilter:是个异常过滤器,用来处理在认证授权过程中抛出的异常

spring跳过登录认证 基于spring的用户登录验证_spring跳过登录认证_06



UsernamePasswordAuthenticationFilter :对/login 的 POST 请求做拦截,校验表单中用户名,密码。




spring跳过登录认证 基于spring的用户登录验证_spring跳过登录认证_07


自从有了springboot之后,对于spring security提供了自动化配置方案,可以使用较少的配置来使用spring security

两个重要的接口

UserDetailsService 接口

查询数据库用户名和密码的过程

1、创建类继承UserNamePasswordAuthenticationFilter,重写是三个方法

2、创建类实现UserDetailsService 接口,编写查询过程,返回User对象,这个User对象是框架自带的

PasswordEncoder接口

用于返回User中密码的加密

Web权限方案

用户认证
设置登录的用户名和密码
1)通过配置文件

spring跳过登录认证 基于spring的用户登录验证_安全_08

2)通过配置类


package com.jn.security.config;

import org.springframework.context.annotation.Bean;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author 江南大学1033190417
 * @date 2022/2/4 22:22
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123456");//加密
        auth.inMemoryAuthentication().withUser("wy").password(password).roles("admin");
    }

    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();//默认没有
    }
}


3)自定义编写实现类

第一步:创建配置类,设置使用哪个userDetailService实现类


package com.jn.security.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author 江南大学1033190417
 * @date 2022/2/4 22:36
 */
@Configuration
public class SecurityConfig1 extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());

    }

    @Bean
    PasswordEncoder password() {
        return new BCryptPasswordEncoder();
    }
}


第二步:编写实现类,返回User对象,user对象有用户名密码和操作权限


package com.jn.security.service.impl;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author 江南大学1033190417
 * @date 2022/2/4 22:47
 */
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {


    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //模拟查询数据库
        List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        return new User("msjava",new BCryptPasswordEncoder().encode("123"),auths);
    }
}


整合数据库


@Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //模拟查询数据库
        QueryWrapper<com.jn.security.entity.User> wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        com.jn.security.entity.User user=userMapper.selectOne(wrapper);
        if (user==null){
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<GrantedAuthority> auths=AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        return new User(user.getUsername(), new BCryptPasswordEncoder().encode(user.getPassword()), auths);
    }


 自定义登录页

 重写配置类中所继承类的方法


@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/login.html")//自定义自己的登录页
                .loginProcessingUrl("/user/login")//登录访问路径
                .defaultSuccessUrl("/test/index").permitAll()//登陆成功后跳转路径
                .and().authorizeRequests().antMatchers("/","/test/hello","/user/login").permitAll()//放行路径,不需要认证
                .anyRequest().authenticated()
                .and().csrf().disable();//关闭csrf防护,后面会提到
    }


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/user/login" method="post">
    用户名:<input type="text" name="username"/><!--注意name必须是username和password-->
    <br>
    密  码:<input type="password" name="password">
    <br>
    <input type="submit" value="登录">
</form>
</body>
</html>


 基于角色或权限的访问控制

hasAuthority方法

如果当前用户具有指定的权限,返回true,否则false

第一步:设置访问路径有哪些权限

spring跳过登录认证 基于spring的用户登录验证_前端_09

也可以指定多个角色

spring跳过登录认证 基于spring的用户登录验证_spring跳过登录认证_10

 第二步:在UserDetailsService的接口实现类里对返回的user设置权限

spring跳过登录认证 基于spring的用户登录验证_安全_11

 hasRole方法

如果用户具备给定角色就允许访问,否则出现403,如果当前用户具有指定角色,返回true

与上一个区别,看源码

spring跳过登录认证 基于spring的用户登录验证_spring_12

 

spring跳过登录认证 基于spring的用户登录验证_前端_13

 会在设置的权限前加ROLE字段,所以在userDetailsService中设置权限时要加上

spring跳过登录认证 基于spring的用户登录验证_前端_14

 有hasRole就有hasAnyRole

 自定义无权限页面

默认页面

spring跳过登录认证 基于spring的用户登录验证_spring_15

非常的丑

只要在配置类中配置一下就好了

spring跳过登录认证 基于spring的用户登录验证_安全_16

 注解的使用

@Secured

判断是有具有角色,注意这里匹配的角色前要加ROLE_,如果有这个角色,就可以访问这个方法

首先要在启动类中开启功能

spring跳过登录认证 基于spring的用户登录验证_spring_17

spring跳过登录认证 基于spring的用户登录验证_spring跳过登录认证_18

 @PreAuthorize

进入方法前的权限认证,可以将登录用户的roles/permissions参数传递到方法中

首先也要开启注解功能

spring跳过登录认证 基于spring的用户登录验证_前端_19

spring跳过登录认证 基于spring的用户登录验证_前端_20

 @PostAuthorize

在方法执行之后进行权限认证,适合验证带有返回值的

先开器注解功能

spring跳过登录认证 基于spring的用户登录验证_spring_21

 用法和上一个类似

@PostFilter

权限认证之后对数据进行过滤

@PreFilter

对传入的参数做过滤

 自定义对出登录

spring跳过登录认证 基于spring的用户登录验证_前端_22

自动登录

使用cookie

原理

spring跳过登录认证 基于spring的用户登录验证_java_23

第一步:创建爱你数据库表,也可以用框架自带的自动创建

spring跳过登录认证 基于spring的用户登录验证_spring_24

第二步:配置类,注入数据源,配置操作数据库对象


@Autowired
    private DataSource dataSource;
    
    //配置对象
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository=new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(true);//自动创建表
        return jdbcTokenRepository;
    }


第三步:配置类配置自动登录


.and().rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60)//设置有效时长(秒)
.userDetailsService(userDetailsService)


第四步:在登录页面添加复选框


<input type="checkbox" name="remember-me">自动登录<!--name必须是这个-->


 CSRF理解

跨域请求伪造,默认开启保护,只对POST,PUT,DELETE保护

在登录页中加入隐藏域


<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">