当前位置: 首页 > 工具软件 > JPA Security > 使用案例 >

SpringSecurity+SpringBoot2.0.4+JPA

糜宜民
2023-12-01

简介:

    一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方式的安全框架。简单的说就是访问权限控制。

    应用的安全性包括:用户认证(Authentication)和用户授权(Authorization)

框架原理:

    对web资源保护 --------->Filter

    对方法进行保护 --------->AOP

主要过滤器:

  • WebAsyncManagerIntegrationFilter
  • SecurityContextPersistenceFilter
  • HeaderWriterFilter
  • CorsFilter
  • LogoutFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • AnonymousAuthentivationFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor
  • UsernamePasswordAuthenticationFilter
  • BasicAuthticationFilter

框架核心组件:

  • SecurityContextHolder :提供对SecurityContext的访问
  • SecurityContext :持有Authentication对象和其他可能需要的信息
  • AuthenticationManager其中可以包含多个AuthenticationProvider
  • ProviderManager对象为AuthenticationManager接口的实现类
  • AuthenticationProvider主要用来进行认证操作的类,调用其中的authenticate()方法去进行认证操作
  • Authentication:SpringSecurity方式的认证主体
  • GrantedAuthority:对认证主体的应用层面的授权,含当前用户的权限信息。通常使用角色表示
  • UserDetail:构建Authentication对象必须的信息,可以自定义,可能需要访问数据库得到
  • UserDetailService:通过username构建UserDetail对象,通过loadUserByUsername根据userName获取UserDetail对象

自定义安全配置的加载机制:

1.添加依赖

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

2.配置yml文件

server:
  port: 8081
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/three_point
    driver-class-name: com.mysql.jdbc.Driver
  jpa:
    hibernate:
      #使用了JPA的自动建表,根据实体类自动生成数据表(需要先创建数据库)
      ddl-auto: update
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    open-in-view: false

3.实体类

//用户类
@Entity
@Table
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String username;
    private String password;
    @ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
    private List<Role> roles;
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
}
//角色类
@Entity
@Table
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
}

4.Dao层,使用jpa操作

@Repository
public interface UserRepository extends JpaRepository<User,Integer> {
    User findByUsername(String username);
}

UserDetailService实现类,用于Security查询角色进行认证,从数据库中获取用户权限信息

/**
* 这里使用的是User不继承UserDetails这个接口,而将添加认证用户的操作放在了上面这个类中,还有另外一种实现方式
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {


    @Autowired
    private UserRepository userRepository;


    /**
     * loadUserByUsername方法重写
     * @param s
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(s);
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role:
             user.getRoles()) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),authorities);
    }
}

5.Security配置类

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    /**
     * 获取到数据库中的用户信息
     * @return
     */
    @Bean
    UserDetailsService detailsService(){
        return new UserDetailsServiceImpl();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /**
         * passwordEncoder(passwordEncoder())里面的passwordEncoder()为我们定义的bean
         * 这样在登录的时候就会使用我们选择编码方式进行验证
         */
        auth.userDetailsService(detailsService()).passwordEncoder(passwordEncoder());
    }


    /**
     * 需要忽略掉的web资源
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/config/**","/css/**","/fonts/**","/img/**","/js/**");
    }


    /**
     * 决定哪些请求会被拦截以及请求该怎么处理
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //项目的主路径可以放行
                .antMatchers("/").permitAll()
                //注册可以放行
                .antMatchers("/registry").permitAll()
                //其他请求必须要经过验证
                .anyRequest().authenticated()
                //注销允许访问
                .and().logout().permitAll()
                //允许表单登陆
                .and().formLogin()
        ;
        //关闭默认的csrf认证
        http.csrf().disable();
    }


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

6.Controller控制层

@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")

@PreAuthorize("#id<10 and principal.username.equals(#username) and #user.username.equals('abc')")

@PostAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")

@PostAuthorize("returnObject%2==0") //判断返回的id是否是双数

这两个注解支持and or 表达式 @PreAuthorize是在方法调用前进行权限检查  @PostAuthorize是在方法调用完成后进行权限检查

@PreFilter("filterObject%2==0")

@PostFilter("filterObject%4==0")

@PreFilter是对集合类型的参数进行过滤  @PostFilter是对集合类型的返回值进行过滤

@Controller
@EnableGlobalMethodSecurity(prePostEnabled = true)//启动@PreAuthorize()注解的作用
public class IndexController {


    @Autowired
    private UserRepository userRepository;


    @Autowired
    private PasswordEncoder passwordEncoder;


    @RequestMapping("/")
    @ResponseBody
    public String home(){
        return "hello spring boot";
    }
    @RequestMapping("/hello")
    @ResponseBody
    public String hello(){
        return "hello world";
    }
    @RequestMapping("/registry")
    @ResponseBody
    public String registry(User user){
        userRepository.save(new User(user.getUsername(),passwordEncoder.encode(user.getPassword())));
        return "save successful!";
    }
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
    @RequestMapping("roleAuth")
    @ResponseBody
    public String roleAuth(){
        return "hello Auth";
    }
    @PreAuthorize("#id<10 and principal.username.equals(#username)" +
            "and #user.username.equals('abc')")
    @RequestMapping("/test")
    public String test(Integer id,String username,User user){
        return "hello test";
    }
    /**
     * @PostAuthroized实在方法调用后检查权限
     * @param id
     * @return
     */
    @PostAuthorize("returnObject%2==0") //判断返回的id是否是双数
    @RequestMapping("/test1")
    public Integer test1(Integer id){
        // ....
        return id;
    }


    /**
     * @PreFilter("filterObject%2==0")
     * 对集合类型的参数进行过滤
     * @PostFilter("filterObject%4==0")
     * 对集合类型的返回值进行过滤
     * @param idList
     * @return
     */
    @PreFilter("filterObject%2==0")
    @PostFilter("filterObject%4==0")
    @RequestMapping("/test2")
    public List<Integer> test2(List<Integer> idList){
        // ....省略部分代码
        return idList;
    }
}

总结:

    优点:提供了一套安全的框架,并且这个框架是可以使用的。提供了很多用户认证的功能,实现相关接口即可,节约大量开发工作。基于spring,易于集成到spring项目中,且封装了许多方法

    缺点:配置文件比较多,角色被”编码“到配置文件和源文件中,RBAC不明显。对于系统中用户,角色,权限之间的关系,没有可操作的界面。大数据量的情况下,springsecurity几乎不可用

 

 类似资料: