Security整合spring boot

  • 2019 年 11 月 6 日
  • 筆記

1、基础概念

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring的IOC,DI,AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为安全控制编写大量重复代码的工作。

2、核心API解读

1、SecurityContextHolder

最基本的对象,保存着当前会话用户认证,权限,鉴权等核心数据。SecurityContextHolder默认使用ThreadLocal策略来存储认证信息,与线程绑定的策略。用户退出时,自动清除当前线程的认证信息。

初始化源码:明显使用ThreadLocal线程。

private static void initialize() {      if (!StringUtils.hasText(strategyName)) {          strategyName = "MODE_THREADLOCAL";      }      if (strategyName.equals("MODE_THREADLOCAL")) {          strategy = new ThreadLocalSecurityContextHolderStrategy();      } else if (strategyName.equals("MODE_INHERITABLETHREADLOCAL")) {          strategy = new InheritableThreadLocalSecurityContextHolderStrategy();      } else if (strategyName.equals("MODE_GLOBAL")) {          strategy = new GlobalSecurityContextHolderStrategy();      } else {          try {              Class<?> clazz = Class.forName(strategyName);              Constructor<?> customStrategy = clazz.getConstructor();              strategy = (SecurityContextHolderStrategy)customStrategy.newInstance();          } catch (Exception var2) {              ReflectionUtils.handleReflectionException(var2);          }      }      ++initializeCount;  }

2、Authentication

源码

public interface Authentication extends Principal, Serializable {      Collection<? extends GrantedAuthority> getAuthorities();      Object getCredentials();      Object getDetails();      Object getPrincipal();      boolean isAuthenticated();      void setAuthenticated(boolean var1) throws IllegalArgumentException;  }

源码分析

1)、getAuthorities,权限列表,通常是代表权限的字符串集合;
2)、getCredentials,密码,认证之后会移出,来保证安全性;
3)、getDetails,请求的细节参数;
4)、getPrincipal, 核心身份信息,一般返回UserDetails的实现类。

3、UserDetails

封装了用户的详细的信息。

public interface UserDetails extends Serializable {      Collection<? extends GrantedAuthority> getAuthorities();      String getPassword();      String getUsername();      boolean isAccountNonExpired();      boolean isAccountNonLocked();      boolean isCredentialsNonExpired();      boolean isEnabled();  }

4、 UserDetailsService

实现该接口,自定义用户认证流程,通常读取数据库,对比用户的登录信息,完成认证,授权。

public interface UserDetailsService {      UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;  }

5、 AuthenticationManager

认证流程顶级接口。可以通过实现AuthenticationManager接口来自定义自己的认证方式

Spring提供了一个默认的实现,ProviderManager。

public interface AuthenticationManager {      Authentication authenticate(Authentication var1) throws AuthenticationException;  }

3.整合

1、流程描述

1)、模拟增删改查  2)、未登录授权都不可以访问  3)、登录后根据用户权限,访问指定页面  4)、对于未授权页面,访问返回403:资源不可用

2、核心依赖

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

3、核心配置

package com.Boot.config;    import com.Boot.Security.UserDetailServiceImpl;  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.HttpSecurity;  import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;  import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;  import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;    /**   * @Classname SpringSecurityConfig   * @Description TODO   * @Date 2019/11/6 19:59   * @Created by 远   */  @Configuration  @EnableWebSecurity //启动springSecurity启动链  public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {        /**       * @return       * @Author 远       * @Description 代替认证管理器       * @Param       **/      /**       * 自定义认证数据源       */      @Override      protected void configure(AuthenticationManagerBuilder builder) throws Exception{          builder.userDetailsService(userDetailService())                  .passwordEncoder(passwordEncoder());      }      @Bean      public UserDetailServiceImpl userDetailService (){          return new UserDetailServiceImpl () ;      }      /**       * 密码加密       */      @Bean      public BCryptPasswordEncoder passwordEncoder(){          return new BCryptPasswordEncoder();      }        /**       * @Author 远       * @Description 硬编码一个用户用于测试       * @Param       * @return       **/      /*@Override      protected void configure(AuthenticationManagerBuilder auth) throws Exception {          auth.inMemoryAuthentication().withUser("ljw").password("123").authorities("ROLE_ADD");      }*/        /**       * @return       * @Author 远       * @Description 代替之前配置<security:http></security:http>       **/      @Override      protected void configure(HttpSecurity http) throws Exception {            http.authorizeRequests()              //antMatchers指定页面,hasAnyAuthority指定拥有什么样的权限的用户可以访问                  .antMatchers("/add").hasAnyAuthority("ROLE_ADD")                  .antMatchers("/update").hasAnyAuthority("ROLE_UPDATE")                  .antMatchers("/list").hasAnyAuthority("ROLE_SELECT")                  .antMatchers("/delete").hasAnyAuthority("ROLE_DELETE")              //放行index和login页面                  .antMatchers("/login").permitAll()                  .antMatchers("/index").permitAll()              //拦截全部                  .antMatchers("/**")                  .fullyAuthenticated()                  .and()              //解决页面csrf报错                  .csrf().disable();           // 配置登录功能          http.formLogin().usernameParameter("user")                  .passwordParameter("pwd")                  .loginPage("/userLogin");          // 注销成功跳转首页          http.logout().logoutSuccessUrl("/");          //开启记住我功能          http.rememberMe().rememberMeParameter("remeber");      }  }  

4、认证

package com.Boot.Security;      import org.springframework.security.core.GrantedAuthority;  import org.springframework.security.core.authority.SimpleGrantedAuthority;  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.stereotype.Service;    import javax.annotation.Resource;  import java.util.ArrayList;  import java.util.List;    /**   * @Classname UserDetailServiceImpl   * @Description 认证   * @Date 2019/11/6 22:22   * @Created by 远   */  @Service  public class UserDetailServiceImpl implements UserDetailsService {      @Resource      private UserRoleMapper userRoleMapper ;      @Override      public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {          // 这里可以捕获异常,使用异常映射,抛出指定的提示信息          // 用户校验的操作          // 假设密码是数据库查询的 123          String password = "$2a$10$XcigeMfToGQ2bqRToFtUi.sG1V.HhrJV6RBjji1yncXReSNNIPl1K";          // 假设角色是数据库查询的          //查询数据里所有的用户对应角色的权限          List<String> roleList = userRoleMapper.selectByUserName(username) ;          List<GrantedAuthority> grantedAuthorityList = new ArrayList<>() ;          /*           * Spring Boot 2.0 版本踩坑           * 必须要 ROLE_ 前缀, 因为 hasRole("LEVEL1")判断时会自动加上ROLE_前缀变成 ROLE_LEVEL1 ,           * 如果不加前缀一般就会出现403错误           * 在给用户赋权限时,数据库存储必须是完整的权限标识ROLE_LEVEL1           */          if (roleList != null && roleList.size()>0){              for (String role : roleList){                  //将所有的角色遍历到GrantedAuthority                  grantedAuthorityList.add(new SimpleGrantedAuthority(role)) ;              }          }          //返回用户,用户的用户名,密码,以及权限          return new User(username,password,grantedAuthorityList);      }  }

5、接口

package com.Boot.Controller;    import org.springframework.stereotype.Controller;  import org.springframework.web.bind.annotation.GetMapping;  import org.springframework.web.bind.annotation.RequestMapping;    /**   * @Classname productController   * @Description 增删改查   * @Date 2019/11/1 14:41   * @Created by 远   */      @Controller  public class productController {      /**       * @Author 远       * @Description       * @Param       * @return       **/      @RequestMapping("/add")      public String add() {          return "product/add";      }      /**       * @Author 远       * @Description 商品修改       **/      @RequestMapping("update")      public String Update() {          return "product/update";      }        /**       * @Author 远       * @Description 商品显示       **/      @RequestMapping("list")      public String list() {          return "product/list";      }        /**       * @Author 远       * @Description 商品删除       **/      @RequestMapping("delete")      public String del() {          return "product/delete";      }       @RequestMapping("/login")      public String login(){          return "login";      }      @RequestMapping("index")      public String index(){          return"index";      }      @RequestMapping("403")      public String forbidden(){          return "403";      }        }  

6、前端登录页面

这里要和Security的配置文件相对应。

<div align="center">      <form th:action="@{/userLogin}" method="post">          用户名:<input name="user"/><br>          密&nbsp;&nbsp;&nbsp;码:<input name="pwd"><br/>          <input type="checkbox" name="remeber"> 记住我<br/>          <input type="submit" value="Login">      </form>  </div>