标签搜索

SpringSecurity简单搭建

admin
2025-04-03 / 0 评论 / 0 阅读 / 正在检测是否收录...

- 添加 Spring Security 依赖

首先我默认大家都已经了解 Spring Boot 了,在 Spring Boot 项目中添加依赖是非常简单的.把对应的
spring-boot-starter-* 加到pom.xml 文件中就行了

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

- 配置 Spring Security

简单的使用 Spring Security 只要配置三个类就完成了,分别是:

  • UserDetails
    这个接口中规定了用户的几个必须要有的方法

    public interface UserDetails extends Serializable {
     
      //返回分配给用户的角色列表
      Collection<? extends GrantedAuthority> getAuthorities();
      
      //返回密码
      String getPassword();
     
      //返回帐号
      String getUsername();
     
      // 账户是否未过期
      boolean isAccountNonExpired();
     
      // 账户是否未锁定
      boolean isAccountNonLocked();
     
      // 密码是否未过期
      boolean isCredentialsNonExpired();
     
      // 账户是否激活
      boolean isEnabled();
    }
  • UserDetailsService
    这个接口只有一个方法 loadUserByUsername,是提供一种用 用户名 查询用户并返回的方法。

    public interface UserDetailsService {
      UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
    }
  • WebSecurityConfigurerAdapter
    这个内容很多,就不贴代码了,大家可以自己去看.
    我们创建三个类分别继承上述三个接口
  • 此 User 类不是我们的数据库里的用户类,是用来安全服务的.

     * Created by Yuicon on 2017/5/14.
     * https://segmentfault.com/u/yuicon
     */
    public class User implements UserDetails {
     
      private final String id;
      //帐号,这里是我数据库里的字段
      private final String account;
      //密码
      private final String password;
      //角色集合
      private final Collection<? extends GrantedAuthority> authorities;
     
      User(String id, String account, String password, Collection<? extends GrantedAuthority> authorities) {
          this.id = id;
          this.account = account;
          this.password = password;
          this.authorities = authorities;
      }
     
      //返回分配给用户的角色列表
      @Override
      public Collection<? extends GrantedAuthority> getAuthorities() {
          return authorities;
      }
     
      @JsonIgnore
      public String getId() {
          return id;
      }
     
      @JsonIgnore
      @Override
      public String getPassword() {
          return password;
      }
      
      //虽然我数据库里的字段是 `account`  ,这里还是要写成 `getUsername()`,因为是继承的接口
      @Override
      public String getUsername() {
          return account;
      }
      // 账户是否未过期
      @JsonIgnore
      @Override
      public boolean isAccountNonExpired() {
          return true;
      }
      // 账户是否未锁定
      @JsonIgnore
      @Override
      public boolean isAccountNonLocked() {
          return true;
      }
      // 密码是否未过期
      @JsonIgnore
      @Override
      public boolean isCredentialsNonExpired() {
          return true;
      }
      // 账户是否激活
      @JsonIgnore
      @Override
      public boolean isEnabled() {
          return true;
      }
    }
  • 继承 UserDetailsService

    
    Created by Yuicon on 2017/5/14.
    
    
    https://segmentfault.com/u/yuicon
    */
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
    // jpa
    @Autowired
    private UserRepository userRepository;
    /**
    
    
    提供一种从用户名可以查到用户并返回的方法
    
    
    @param account 帐号
    
    
    @return UserDetails
    
    
    @throws UsernameNotFoundException
    */
    @Override
    public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {
    // 这里是数据库里的用户类
    User user = userRepository.findByAccount(account);
    if (user == null) {
    throw new UsernameNotFoundException(String.format(没有该用户 '%s'., account));
    } else {
    //这里返回上面继承了 UserDetails  接口的用户类,为了简单我们写个工厂类
    return UserFactory.create(user);
    }
    }
    }
    
    
  • UserDetails 工厂类

    
    Created by Yuicon on 2017/5/14.
    
    
    https://segmentfault.com/u/yuicon
    */
    final class UserFactory {
    private UserFactory() {
    }
    static User create(User user) {
    return new User(
    user.getId(),
    user.getAccount(),
    user.getPassword(),
    mapToGrantedAuthorities(user.getRoles().stream().map(Role::getName).collect(Collectors.toList()))
    );
    }
    //将与用户类一对多的角色类的名称集合转换为 GrantedAuthority 集合
    private static List<GrantedAuthority> mapToGrantedAuthorities(List<String> authorities) {
    return authorities.stream()
    .map(SimpleGrantedAuthority::new)
    .collect(Collectors.toList());
    }
    }
    
  • 重点, 继承 WebSecurityConfigurerAdapter 类

    
    Created by Yuicon on 2017/5/14.
    
    
    https://segmentfault.com/u/yuicon
    */
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    // Spring会自动寻找实现接口的类注入,会找到我们的 UserDetailsServiceImpl  类
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
    authenticationManagerBuilder
    // 设置UserDetailsService
    .userDetailsService(this.userDetailsService)
    // 使用BCrypt进行密码的hash
    .passwordEncoder(passwordEncoder());
    }
    // 装载BCrypt密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
    }
    //允许跨域
    @Bean
    public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurerAdapter() {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping(/**).allowedOrigins(*)
    .allowedMethods(GET, HEAD, POST,PUT, DELETE, OPTIONS)
    .allowCredentials(false).maxAge(3600);
    }
    };
    }
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
    // 取消csrf
    .csrf().disable()
    // 基于token,所以不需要session
    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
    .authorizeRequests()
    .antMatchers(HttpMethod.OPTIONS, /).permitAll()
    // 允许对于网站静态资源的无授权访问
    .antMatchers(
    HttpMethod.GET,
    /,
    /*.html,
    /favicon.ico,
    //.html,
    /**/.css,
    //*.js,
    /webjars/,
    /swagger-resources/,
    /*/api-docs
    ).permitAll()
    // 对于获取token的rest api要允许匿名访问
    .antMatchers(/auth/).permitAll()
    // 除上面外的所有请求全部需要鉴权认证
    .anyRequest().authenticated();
    // 禁用缓存
    httpSecurity.headers().cacheControl();
    }
    }
    

    - 控制权限到 controller

    使用 @PreAuthorize("hasRole('ADMIN')") 注解就可以了

    /**
    
    在 @PreAuthorize 中我们可以利用内建的 SPEL 表达式:比如 'hasRole()' 来决定哪些用户有权访问。
    
    
    需注意的一点是 hasRole 表达式认为每个角色名字前都有一个前缀 'ROLE_'。所以这里的 'ADMIN' 其实在
    
    
    数据库中存储的是 'ROLE_ADMIN' 。这个 @PreAuthorize 可以修饰Controller也可修饰Controller中的方法。
    **/
    @RestController
    @RequestMapping(/users)
    @PreAuthorize(hasRole('USER')) //有ROLE_USER权限的用户可以访问
    public class UserController {
    @Autowired
    private UserRepository repository;
    @PreAuthorize(hasRole('ADMIN'))//有ROLE_ADMIN权限的用户可以访问
    @RequestMapping(method = RequestMethod.GET)
    public List<User> getUsers() {
    return repository.findAll();
    }
    }
    

    - 结语

0

评论 (0)

取消