Spring Security

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

SpringSecurity的简单使用

  • pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>SpringSecurcityDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>        <!-- 1.5.1-->
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
package com.rj.bd.Conction;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

/**
 * @author LXY
 * @desc
 * @time 2023--04--15--10:42
 */
@RestController
@RequestMapping("/api")
public class TestConction {

    @GetMapping("/add")
    public String queryaccessRoompages() {

        return  "Hello Spring";
    }



}

  • 访问接口的时候会内置弹出登录权限效验,是security内置的一个登录效验,通常情况下,访问add接口是直接返回数据的,加上了security直接跳转了
  • 默认账号是user,密码是控制台输入的
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

用户认证,自定义账号密码

Web权限解决方案:
设置登录的用户名和密码:1.通过配置文件,2.通过配置类,3.自定义编写配置类

  • 第一种方式,通过配置文件
spring:
  security:
    user:
      name: lxy
      password: 123

在这里插入图片描述

  • 通过配置类
package com.rj.bd.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 LXY
 * @desc
 * @time 2023--04--16--15:20
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //重写方法设置用户明码
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //对类进行实例化该类是PasswordEncoder的子类,PasswordEncoder是用户管理密码加密的,为了安全
        final BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        //调用方法返回加密之后的密码
        final String pwd = bCryptPasswordEncoder.encode("798");
        //在acth中存储设置好的账号密码角色等
        auth.inMemoryAuthentication().withUser("lxy").password(pwd).roles("admin");
    }

    //不创建会报错,匹配值报null,因为上面用到了他的子类进行加密,默认是没有这个类的,需要实例化BCryptPasswordEncoder
    @Bean
    PasswordEncoder getpasswordencoder(){
        return  new BCryptPasswordEncoder();
    }
}

在这里插入图片描述

  • 自定义配置类
  • 自定义实现UserDetailsService接口,实现里面的方法,注入账号和加密的密码,权限,
  • SecurityConfigTest 在将自定义实现的类注入进去
package com.rj.bd.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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author LXY
 * @desc
 * @time 2023--04--16--15:41
 */
@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {

    @Autowired
   public UserDetailsService userDetailsService;

    //重写方法设置用户明码
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //装入UserDetailsService的实现类,自定义的饿实现类,后面加上PasswordEncoder的加密实现类
        auth.userDetailsService(userDetailsService).passwordEncoder(getpasswordencoder());
    }

    //不创建会报错,匹配值报null,因为上面用到了他的子类进行加密,默认是没有这个类的,需要实例化BCryptPasswordEncoder
    @Bean
    PasswordEncoder getpasswordencoder(){
        return  new BCryptPasswordEncoder();
    }

}

package com.rj.bd.Config;

import org.springframework.security.access.annotation.Secured;
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 LXY
 * @desc
 * @time 2023--04--16--15:44
 */
@Service("userDetailsService")
public class MyDetailServiceConcif implements UserDetailsService {

    //这个就是登录的方法
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //权限相关的list集合
        List<GrantedAuthority>roles= AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
        //需要返回一个实例化的user
        //user里面放三个参数,一个账号,一个密码,一个权限的集合
        return new User("lll",new BCryptPasswordEncoder().encode("789"),roles);
    }
}

数据库查询账号密码

  • 模拟一个数据库查询数据的方法,从里面获取数据,创建了一个实体类
  • 套路还是一样的,如果账号正确直接返回,如果账号错误,抛异常就算登录失败。完成了数据库查询账号密码
package com.rj.bd.Config;

import org.springframework.security.access.annotation.Secured;
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 LXY
 * @desc
 * @time 2023--04--16--15:44
 */
@Service("userDetailsService")
public class MyDetailServiceConcif implements UserDetailsService {

    //这个就是登录的方法
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        com.rj.bd.enity.User user=getuser(username);
        if (user==null){
            throw new UsernameNotFoundException("登录失败,用户不存在");
        }else {
            //权限相关的list集合
            List<GrantedAuthority>roles= AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
            //需要返回一个实例化的user
            //user里面放三个参数,一个账号,一个密码,一个权限的集合
            return new User(user.getUsername(),new BCryptPasswordEncoder().encode(user.getPassword()),roles);
        }


    }

    private com.rj.bd.enity.User getuser(String username) {

        if (username.equals("lxy")){
            com.rj.bd.enity.User user=new com.rj.bd.enity.User();
            user.setId(001);
            user.setUsername("lxy");
            user.setPassword("789");
            return user;
        }else {
            return  null;
        }
    }

}

  • 实体类
package com.rj.bd.enity;

/**
 * @author LXY
 * @desc
 * @time 2023--04--16--15:59
 */
public class User {

    private Integer id;
    private String username;
    private String password;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

自定义登录页面

  • 自定义登录页面 通过重写configure(HttpSecurity http)方法,可以设置自定义的登录页面和指定哪些页面不需要登录就可以跳转,设置的是/api/add和/user/logi不需要认证,直接访问就可以进去
    /login.html作为登录的地址,action=“/user/login” method="post"是必须的
    用户名username和密码password是源码写好了name不对接不到参数
  //通过HttpSecurity参数的configure进行设置
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.formLogin() //自定义编写的登录页面
            .loginPage("/login.html")   //自定义的登录页面的地址
            .loginProcessingUrl("/user/login")  //登录访问地址
            .defaultSuccessUrl("/api/index").permitAll()  //登录成功跳转地址
            .and().authorizeRequests().antMatchers("/","/api/add","/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">
        <br>
        密码:<input type="text" name="password">
        <br>
        <input type="submit" value="login">

</form>

</body>
</html>

在这里插入图片描述

    @GetMapping("/index")
    public String index() {

        return  "Hello index";
    }

在这里插入图片描述

权限访问

  • 对于权限都不陌生,有些东西只有管理员才能访问,有些东西全部都能访问,

hasAuthority()

如果当前的主题具有指定的权限则返回true,否则就返回false,有缺陷,只能有一个权限,不能设置多个权限

  • 下面的代码是通过hasAnyAuthority()去给/api/index 接口去设定权限,只有admin权限的人才可以访问,其他的不能访问

  • 登录账号的时候,有一个List< GrantedAuthority > 后面跟的是admin,如果不是admin就会出错

  • 出错页面:
    -

  • 代码演示:
    在这里插入图片描述

在这里插入图片描述

hasAnyAuthority()

  • 这个是支持多个访问,只要一个权限对应就可访问
    在这里插入图片描述

hasRole()

  • hasRole返回的数据是有一个前缀功能的,如果没有前缀就会出错,这个也只能设置一个权限

在这里插入图片描述
在这里插入图片描述

自定义无权限返回页面

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注解的使用

  • 第一步是在启动类上添加注解的支持
    在这里插入图片描述

    1. 修改用户权限
      在这里插入图片描述
    1. 启动类上添加注解
  • @Secured注解的功能是当用户拥有某个角色就可以继续访问
    在这里插入图片描述

  • 第二个注解是PreAuthorize
    首先添加对注解的支持,这个也是登录之前对身份进行效验

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • @PostAuthorize注解,是先执行在效验,适合有返回值的方法,用的比较少
  • 虽然最后还是没有权限,但是方法中执行了

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
使用 @PreFilter 和 @PostFilter 可以对集合类型的参数或返回值进行过滤。使用 @PreFilter 和 @PostFilter 时,Spring Security 将移除使对应表达式的结果为 false 的元素。

 @PostFilter("filterObject.id%2==0")
  public List<User> findAll() {
      List<User> userList = new ArrayList<User>();
      User user;
      for (int i=0; i<10; i++) {
         user = new User();
         user.setId(i);
         userList.add(user);
      }
      return userList;
   }

代码表示对返回结果中 id 不为偶数的 user 进行移除。filterObject 是使用 @PreFilter 和 @PostFilter 时的一个内置表达式,表示集合中的当前对象。当 @PreFilter 标注的方法拥有多个集合类型的参数时,需要通过 @PreFilter 的 filterTarget 属性指定当前 @PreFilter 是针对哪个参数进行过滤的
如下面代码就通过 filterTarget 指定了当前 @PreFilter 是用来过滤参数 ids 的。

@PreFilter(filterTarget="ids", value="filterObject%2==0")
public void delete(List<Integer> ids, List<String> usernames) {
      ...
}

用户登出

  • 用户登出之后是不能访问网页的,来设置一下

在这里插入图片描述
在这里插入图片描述

  • 试验:
    在这里插入图片描述
    在这里插入图片描述
  • 现在访问的时候不需要权限验证了,点击退出之后在访问接口就没有权限了,这样就成功了
    在这里插入图片描述

更改请求为mysql查询

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

server:
  port: 8080
#spring:
#  security:
#    user:
#      name: lxy
#      password: 123

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/serucity?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&AllowPublicKeyRetrieval=True
    username: root
    password: root

自动登录

  • 用户登录之后关掉浏览器,在打开还可以实现自动登录
    在这里插入图片描述
    在这里插入图片描述

  • 他的父类中认证成功会调用一个成功的方法
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 这个类就是用来操作mysql的自带的类,存放token对toke
    在这里插入图片描述

  • 首先新建Mysql

-- ---------------
-- 自动登录
-- ---------------
CREATE TABLE `persistent_logins` (
    `series` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '主键',
    `username` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '用户名',
    `token` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'token',
    `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后使用时间',
    PRIMARY KEY (`series`)
)
  • 然后注入数据源,security已经封装好了,所以需要将JdbcTokenRepositoryImpl 的数据源注入过去

    //注入数据源
    @Autowired
    private DataSource dataSource;

    //配置对象
    public PersistentTokenRepository getpersistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository=new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return  jdbcTokenRepository;
    }

在这里插入图片描述

  .and().rememberMe().tokenRepository(getpersistentTokenRepository())  //将getpersistentTokenRepository()注入开启自动登录
                .tokenValiditySeconds(60)//保存市场以秒为单位
                .userDetailsService(userDetailsService)   //将userDetailsService注入进来

在这里插入图片描述

  • 最后一步,选一个复选框,要用指定的name,不然无法获取到

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值