一:介绍

1.定义

  Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框 架。Spring Security 主要实现了Authentication(认证,解决who are you? ) 和 Access Control(访问控制,也就是what are you allowed to do?,也称为Authorization)。Spring Security在架构上将认证与授权分离,并提供了扩展点。

 

2.解释

认证 :用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录,二维码登录,手机短信登录,指纹认证等方式。
授权: 授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。

 

3.springsecurity与shrio

  优点:
  1:Spring Security基于Spring开发,项目中如果使用Spring作为基础,配合Spring Security做权限更加方便,而Shiro需要和Spring进行整合开发
  2:Spring Security功能比Shiro更加丰富些,例如安全防护
  3:Spring Security社区资源比Shiro丰富
  缺点:
  1:Shiro的配置和使用比较简单,Spring Security上手复杂
  2:Shiro依赖性低,不需要任何框架和容器,可以独立运行,而Spring Security依赖于Spring容器

 

二:使用

1.用户身份认证

  1.1 默认方式:

   引用pom

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

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

  编写contoller:

package com.jun.security.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin")
public class AdminController {
    @GetMapping("/demo")
    public String demo() {
        return "spring security demo";
    }
}

  引入Spring Security之后 ,访问 API 接口时,需要首先进行登录,才能进行访问。 测试 http://localhost:8080/admin/demo ,会跳转到

  

  需要登录,默认用户名:user,密码可以查看控制台日志获取

Using generated security password: fc858c10-51e3-4055-b653-ef9a141861da

 

  登录之后跳转到:

  

 

 

  1.2 基于yml配置文件设置用户名和密码:

spring:
  security:
    user:
      name: caojun
      password: 123456
      roles: ADMIN

  可以使用配置的用户名和密码配置。

  原理: 默认情况下,UserDetailsServiceAutoConfiguration自动化配置类,会创建一个内存级别的 InMemoryUserDetailsManager对象,提供认证的用户信息。

添加 spring.security.user 配置项,UserDetailsServiceAutoConfiguration 会基于配置的信息在内存中创建一个用户User。
未添加 spring.security.user 配置项,UserDetailsServiceAutoConfiguration 会自动在内存中创建一个用户名为 user,密码为 UUID 随机的用户 User。

 

  1.3 基于UserDetailService接口:

  先上加密文件:

package com.jun.security.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class WebSecurityConfig {
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

  然后写userDetailService:

package com.jun.security.service;

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.BCrypt;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String password = passwordEncoder.encode("123456");
        // 支持的加密方式可以通过PasswordEncoderFactories查看
        return User
                .withUsername("caojunjun")
                .password(password)
                .authorities("admin").build();
    }
}

 

  1.4 WebSecurityConfigureAdapter配置类

  创建 WebSecurityConfig配置类,继承 WebSecurityConfigurerAdapter抽象类,实现 Spring Security 在 Web 场景下的自定义配置

package com.jun.security.config;

import com.jun.security.service.UserDetailsServiceImpl;
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.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.annotation.Resource;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private UserDetailsServiceImpl userDetailsService;
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 1.直接引入userDetailsService
        auth.userDetailsService(userDetailsService);
    }
}

 

  1.5 WebSecurityConfigureAdapter配置类,内存篇日志方式

package com.jun.security.config;

import com.jun.security.service.UserDetailsServiceImpl;
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.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.annotation.Resource;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private UserDetailsServiceImpl userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 1.直接引入userDetailsService
//        auth.userDetailsService(userDetailsService);

        // 2.使用内存配置
        auth.inMemoryAuthentication()
                .withUser("jun1").password(passwordEncoder().encode("112233")).roles("admin")
                .and()
                .withUser("jun2").password(passwordEncoder().encode("223344")).roles("user");
    }
}

 

  1.6 数据库访问

配置:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    hikari:
      minimum-idle: 5
      idle-timeout: 600000
      maximum-pool-size: 10
      auto-commit: true
      pool-name: MyHikariCP
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1

  userDeatilsService

package com.jun.security.service;

import com.jun.security.mapper.PermissionMapper;
import com.jun.security.mapper.UserMapper;
import com.jun.security.model.Permission;
import com.jun.security.model.User;
import org.apache.commons.lang3.StringUtils;
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.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private UserMapper userMapper;
    @Autowired
    private PermissionMapper permissionMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("自定义登录逻辑");
        //从mysql查询用户
        User user = getByUsername(username);
        List<GrantedAuthority> authorities = new ArrayList<>();
        if (user != null) {
            List<Permission> permissions = permissionMapper.selectByUserId(user.getId());
            //设置权限
            permissions.forEach(permission -> {
                if (permission != null && !StringUtils.isEmpty(permission.getEnname())) {
                    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getEnname());
                    authorities.add(grantedAuthority);
                }
            });
            // 封装成UserDetails的实现类
            return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
        } else {
            throw new UsernameNotFoundException("用户名不存在");
        }
    }

    public User getByUsername(String username) {
        return userMapper.getByUsername(username);
    }
}

 

2.自定义登陆页面

  配置:

  认证和授权,可以使用默认成功的页面,也可以使用url进行重定向【这里写一下重定向的接口】

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private UserDetailsServiceImpl userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // 1.直接引入userDetailsService
//        auth.userDetailsService(userDetailsService);

    // 2.使用内存配置
//        auth.inMemoryAuthentication()
//                .withUser("jun1").password(passwordEncoder().encode("112233")).roles("admin")
//                .and()
//                .withUser("jun2").password(passwordEncoder().encode("223344")).roles("user");
//    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin() //表单提交
                .loginPage("/login.html") //自定义登录页面
                .loginProcessingUrl("/user/login") //登录访问路径,必须和表单提交接口一样
//                .defaultSuccessUrl("/main.html") //认证成功之后跳转的路径
                .successForwardUrl("/tomain")
                .failureForwardUrl("/toerror");

        http.authorizeRequests()
                //设置哪些路径可以直接访问,不需要认证
                .antMatchers("/user/login", "/login.html").permitAll()
                .anyRequest().authenticated() //需要认证
                .and().csrf().disable(); //关闭csrf防护
    }
}

  重定向接口:

@Controller
public class LoginController {

    @RequestMapping("/tomain")
    public String tomain() {
        return "redirect:/main.html";
    }

    @RequestMapping("/toerror")
    public String toerror() {
        return "redirect:/error.html";
    }

    @RequestMapping("/showLogin")
    public String showLogin() {
        return "login";
    }
}

  登陆页面

<!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="password" name="password"/><br/>
    记住我:<input type="checkbox" name="remember-me" value="true"/><br/>
    <input type="submit" value="提交"/>
</form>
</body>
</html>

 

3.自定义处理器

  成功处理器:

package com.jun.security.handler;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author Fox
 */
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private String redirectUrl;

    public MyAuthenticationSuccessHandler(String redirectUrl) {
        this.redirectUrl = redirectUrl;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.sendRedirect(redirectUrl);
    }
}

 

  失败处理器:

package com.jun.security.handler;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author Fox
 */
public class MyAuthenticationFailHandler implements AuthenticationFailureHandler {

    private String redirectUrl;

    public MyAuthenticationFailHandler(String redirectUrl) {
        this.redirectUrl = redirectUrl;
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.sendRedirect(redirectUrl);
    }
}

 

  配置:

    /**
     * 自定义处理器
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin() //表单提交
                .loginPage("/login.html")                //自定义登录页面
                .loginProcessingUrl("/user/login")      //登录访问路径,必须和表单提交接口一样
                .successHandler(new MyAuthenticationSuccessHandler("/main.html"))
                .failureHandler(new MyAuthenticationFailHandler("/error.html"));

        http.authorizeRequests()
                //设置哪些路径可以直接访问,不需要认证
                .antMatchers("/user/login", "/login.html").permitAll()
                .anyRequest().authenticated() //需要认证
                .and().csrf().disable(); //关闭csrf防护
    }

 

4.

 

 posted on 2023-02-08 23:32  曹军  阅读(78)  评论(0编辑  收藏  举报