SpringSecurity

SpringSecurity 安全框架

是基于Spring的安全管理框架 ,为系统提供声明式安全访问框架功能

安全管理框架两个重要概念 分别是Authentication(认证) 和Authorization(授权)

核心功能:

用户认证

用户授权

攻击防护

创建第一个SpringSecurity项目:

导入依赖

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

导入依赖后0配置已经有安全框架作用

Using generated security password: 2145465d-8915-4275-835b-1b5f56378bbf

控制台会有密码 账号为user 登录后才能访问资源

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.退出

/loginout

3.自定义密码和账户

spring:
  security:
    user:
      name: ry
      password: 123456

自定义用户认证:

1.基于数据库编写数据访问层代码

2.配置密码解析器组件(passwordEncoder)

基础PasswordEncoder


/**
 * @author 冉毓
 */
@Configuration
public class MyPasswordEncoder implements PasswordEncoder {
    /**
     * 对密码进行加密
     * @param rawPassword
     * @return 返回加密的密码
     */
    @Override
    public String encode(CharSequence rawPassword) {
        return rawPassword.toString();
    }

    /**
     *  对密码进行校验
     * @param rawPassword 原始密码
     * @param encodedPassword 数据库已加密的密码
     * @return 校验结果
     */

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return encode(rawPassword).equals(encodedPassword);
    }
}

3.配置用户登录服务组件(UserDetil)

package com.example.springsecurityleanpart1.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.springsecurityleanpart1.entity.User;
import com.example.springsecurityleanpart1.mapper.UserMapper;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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;

/**
 * @author 冉毓
 */

@Service
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查询用户信息

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername,username);
        User user = userMapper.selectOne(wrapper);

        System.out.println("查询-------------------------");
        //如果用户为null 抛出异常
        if (user == null){
            throw new UsernameNotFoundException("用户不存在");
        }
        //如果用户不为null 返回用户信息
        //返回的是子类 User 
        org.springframework.security.core.userdetails.User userDetails =
                new org.springframework.security.core.userdetails.User
                        (user.getUsername(),
                            user.getPassword(),
                                //不需要验证 工具类
                                AuthorityUtils.NO_AUTHORITIES);


        return userDetails;
    }
}

4.自定义用户认证测试

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

认证安全强化:

1.用户表密码加密

2.密码加密器:

BCryptPasswordEncoder 是实现了PasswordEncoder接口

采用了哈希算法SHA-256 + 随机盐 +密钥

package com.example.springsecurityleanpart1.config;

import com.example.springsecurityleanpart1.service.impl.MyUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author 冉毓
 */
@Configuration
public class SecurityConfig {

    /**
     *使用子类加密方式
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public MyUserDetailsService myUserDetailsService(){
        return new MyUserDetailsService();
    }

}

自定义登录页面:

1.配置SecurityFilterChain 过滤链 放入容器中
2.具体配置
1.登录验证
        //用户认证
        http.formLogin(config->{
            //自定义登录页面
            config.loginPage("/user/login")
                   //登录处理器的映射地址
                   .loginProcessingUrl("/user/login")
                   //登录成功后跳转
                   .defaultSuccessUrl("/main")
                   .failureUrl("/loginFail");
                   //自定义用户名密码参数
                   // .usernameParameter("uname")
                   // .passwordParameter("pwd");
        });

2.退出验证
//退出 登录配置
        http.logout(config->{
            config.logoutUrl("/user/logout");
                    // .logoutSuccessUrl("/user/login");
        });

3.配置CSRF防御(不使用token)
http.csrf(configuration->{
            configuration.disable();
        });

完整代码:

@Configuration
public class SecurityConfig {

    /**
     *使用子类加密方式
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public UserDetailsService myUserDetailsService(){
        return new MyUserDetailsService();
    }


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        //用户认证
        http.formLogin(config->{
            //自定义登录页面
            config.loginPage("/user/login")
                   //登录处理器的映射地址
                   .loginProcessingUrl("/user/login")
                   //登录成功后跳转
                   .defaultSuccessUrl("/main")
                   .failureUrl("/loginFail");
                   //自定义用户名密码参数
                   // .usernameParameter("uname")
                   // .passwordParameter("pwd");
        });



        //退出 登录配置
        http.logout(config->{
            config.logoutUrl("/user/logout");
                    // .logoutSuccessUrl("/user/login");
        });



        //参数配置用户认证和授权

        http.authorizeHttpRequests(registry->{
            //配置放行路径
            registry
                    .requestMatchers("/login","/user/login","/css/**","/img/**","/loginFail").permitAll()

                //对于系统中其他资源 需要认证
                    .anyRequest().authenticated();
        });



        http.csrf(configuration->{
            configuration.disable();
        });
        return http.build();

    }



}

注意!!!!

    //登录成功后跳转 但默认跳转之前访问的地址?contine
                   // .defaultSuccessUrl("/main")
                    //强制定向所登录页面
                    .successForwardUrl("/main")

资源权限认证

基于配置类
1.配置类中对资源加权限

        http.authorizeHttpRequests(registry->{
            //配置放行路径
            registry
                    .requestMatchers("/login","/user/login","/css/**","/img/**","/loginFail").permitAll()
                //对于系统中其他资源 需要认证
                    .anyRequest().authenticated()
                    .requestMatchers("/order/manage").hasAuthority("订单管理")
                    .requestMatchers("/user/manage").hasAuthority("用户管理")
                    .requestMatchers("/shop/manage").hasAuthority("商品管理")
                    .requestMatchers("").hasAnyAuthority("订单管理","用户管理","商品管理");
        });

hasAnyAuthority可以指定多个权限

2.给用户赋权
UserDetailsService 组件中加权
    //模拟加权
        List<String> authorities = new ArrayList<>();
        authorities.add("订单管理");
        authorities.add("商品管理");
        //如果用户不为null 返回用户信息
        //返回的是子类 User
        org.springframework.security.core.userdetails.User userDetails =
                new org.springframework.security.core.userdetails.User
                        (user.getUsername(),
                            user.getPassword(),
                                //加权
                                AuthorityUtils.createAuthorityList(authorities));

3.基于角色
  http.authorizeHttpRequests(registry->{
            //配置放行路径
            registry
                    .requestMatchers("/login","/user/login","/css/**","/img/**","/loginFail").permitAll()
                //对于系统中其他资源 需要认证

               
                    .requestMatchers("/order/manage").hasRole("manage")
                    .anyRequest().authenticated()
                   ;
        });

hasRole 加角色

 //模拟加权
 // List<String> authorities = new ArrayList<>();
 // authorities.add("订单管理");
 // authorities.add("商品管理");
 //从数据库查询权限
 List<String> authorities = permissionMapper.selectListByUid(user.getUid());

// 从数据库查角色
 List<String> strings = roleMapper.selectListByUid(user.getUid());
 for(String roles : strings){
     authorities.add("ROLE_"+roles);
 }
 //如果用户不为null 返回用户信息
 //返回的是子类 User
 org.springframework.security.core.userdetails.User userDetails =
         new org.springframework.security.core.userdetails.User
                 (user.getUsername(),
                     user.getPassword(),
                         //加权
                         AuthorityUtils.createAuthorityList(authorities));


 return userDetails;

注:!!!这里 需要加 authorities.add(“ROLE_”+roles);才是加角色

否则只是加权限

基于注解:

@EnableMethodSecurity 注解 开启基于注解方式
 @RequestMapping("/order/manage")
        @ResponseBody
        @PreAuthorize("hasAnyRole('vip')")
        public String orderManage(){
            return "商城订单管理功能";
        }

在 @PreAuthorize注解中使用同配置类加权 在接口上

扩展:

无权限跳转页面:

配置类中

//权限认证失败 跳转页面
http.exceptionHandling(config->{
    config.accessDeniedPage("/user/noAuth");
});

网页记录功能:

1.开启springsecurity中 记住我功能

      //开启记住我功能
        http.rememberMe(config->{
            // 有效时间
            config.tokenValiditySeconds(360000)
                    .rememberMeParameter("remember");

        });

2.在复选框中的name设置为remember

 <div class="checkbox mb-3">
        <label>
            <input type="checkbox" name="remember"> 记住我
        </label>
    </div>

Security整合Thymeleaf

<dependency>
      <groupId>org.thymeleaf.extras</groupId>
      <artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>

命名空间:

<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:th="http://www.thymeleaf.org"
	  xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:th="http://www.thymeleaf.org"
	  xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>好货商城</title>
</head>
<body>
<h1 align="center">欢迎进入好货商城管理后台<span style="padding-left: 50px;font-size: 20px">
	<label sec:authentication="principal.username"></label>&nbsp;&nbsp;<a href="/user/logout">退出登录</a>
</span></h1>
<hr>
<div sec:authorize="hasRole('common')">
	<h3>普通用户</h3>
	<ul>
		<li><a href="/order/manage">订单管理</a></li>
	</ul>
</div>
<div sec:authorize="hasRole('vip')">
	<h3>VIP用户</h3>
	<ul>
		<li><a href="/order/manage">订单管理</a></li>
		<li><a href="/shop/manage">店铺管理</a></li>
	</ul>
</div>
<div sec:authorize="hasRole('manager')">
	<h3>管理员</h3>
	<ul>
		<li><a href="/order/manage">订单管理</a></li>
		<li><a href="/shop/manage">店铺管理</a></li>
		<li><a href="/user/manage">系统用户管理</a></li>
	</ul>
</div>
</body>
</html>

sec:authentication可以的到用户的信息和权限

sec:authorize可以根据用户权限判断

获取认证用户信息:

1.通过控制器HttpSession

   @RequestMapping(value = "/user")
    public String userManage(HttpSession session)
    {
        //获取security上下文
        SecurityContext springSecurityContext = (SecurityContext) session.getAttribute("SPRING_SECURITY_CONTEXT");
        UserDetails userDetails = (UserDetails) springSecurityContext.getAuthentication().getPrincipal();
            userDetails.getUsername();
        return "userManage";
    }

2.通过SecurityContextHolder

   @RequestMapping(value = "/user")
    public String userManage(Authentication authentication)
    {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication contextAuthentication = context.getAuthentication();
        UserDetails userDetails = (UserDetails) contextAuthentication.getPrincipal();

        System.out.println(userDetails.getUsername());
        return "userManage";
    }

3.Authentication 对象

    @RequestMapping(value = "/user")
    public String userManage(Authentication authentication)
    {
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        System.out.println(userDetails.getUsername());
        return "userManage";
    }

CSRF

Cross-site request forgery:跨域请求伪造

1.HTTP Referer记录了该HTTP请求的来源地址。

2.验证码

3.CSRF Token令牌(Spring Security)

(1)用户登录后生成Token,放在用户的Session中

(2)在页面表单中附上Token参数

(3)用户发送请求都需要携带这个Token参数

Security中CSRF防护:

默认开启CSRF项目中所有POST PUT DELETE都会被拦截 需要携带Token参数

//注意修改之前的代码 注释掉以下
        // http.csrf(configuration->{
        //     configuration.disable();
        // });

方法一

表单加隐藏域

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

表单中使用th:action

<form class="form-signin" method="post" th:action="@{/user/login}">

但是!!!!! 退出登录需要改为Post请求
:跨域请求伪造

1.HTTP Referer记录了该HTTP请求的来源地址。

2.验证码

3.CSRF Token令牌(Spring Security)

(1)用户登录后生成Token,放在用户的Session中

(2)在页面表单中附上Token参数

(3)用户发送请求都需要携带这个Token参数

Security中CSRF防护:

默认开启CSRF项目中所有POST PUT DELETE都会被拦截 需要携带Token参数

//注意修改之前的代码 注释掉以下
        // http.csrf(configuration->{
        //     configuration.disable();
        // });

方法一

表单加隐藏域

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

表单中使用th:action

<form class="form-signin" method="post" th:action="@{/user/login}">

但是!!!!! 退出登录需要改为Post请求

  • 23
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值