Spring Security集成与使用

说明

security会对服务端的接口添加权限过滤,不具备权限的请求将被拒绝。

引入security

要引入security,只需添加依赖即可:
在pom.xml中引入security:

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

运行工程,控制台会生成一个随机的密码:
在这里插入图片描述

当用浏览器访问服务端接口时,会提示输入用户名和密码。用户名为user,密码即为生成的随机密码。该验证方式为httpSecurity basic验证。
若要直接调用接口访问,则需要在请求头的Authorization中附加用户名/密码。
在这里插入图片描述

使用postman时,需要将请求的Authorization属性的TYPE设置为Basic Auth

若要关闭该验证功能,则为Application启动类的@SpringBootApplication注解添加排除类SecurityAutoConfiguration.class

@SpringBootApplication(exclude=SecurityAutoConfiguration.class)
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

但该方式的优先级低于配置类。若配置类设置了开启验证,则依然还是需要进行验证。

设置

设置用户名和密码

在application.properties中添加属性:

spring.security.user.name=user
spring.security.user.password=123

这样,就将security验证的用户名和密码修改为user123但这样意味着登录系统的用户名和密码是固定的,不推荐。
若使用配置类,则可以不用在application.properties中添加属性,而是直接在配置类中设置。
配置类优先级更高。若配置类中没有设置用户名和密码,则使用application.properties中的设置。

使用配置类

添加一个配置类:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    // 设置密码验证方式
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 设置对各个接口的访问过滤
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeRequests() // 对请求进行授权
            .antMatchers("/login","/version").permitAll() // 设置不需要认证的请求
            .antMatchers("/test").hasAuthority("p1") // 设置/test接口需要p1权限
            .anyRequest().authenticated() // 设置所有请求都需要认证
            .and() // 上一项配置已经结束,下一项配置将开始
            .httpBasic(); // 开启httpBasic登录
        httpSecurity.csrf().disable(); // 关闭默认csrf认证
    }

    // 设置对静态资源的访问过滤
    @Override
    public void configure(WebSecurity web) throws Exception {
        // 设置不拦截的静态资源
        web.ignoring().antMatchers("/file/**","/images/**");
    }
}

其中:

  • @EnableWebSecurity:开启Security功能,从而支持Security的注解。
  • 若密码不需要使用任何加密,则passwordEncoder()的返回可设置为NoOpPasswordEncoder.getInstance()
  • antMatchers():可传入多个String参数,用于匹配多个请求API。结合permitAll(),可同时对多个请求设置忽略认证。
  • and():用于表示上一项配置已经结束,下一项配置将开始。

然后创建一个Service文件用于Security查询用户信息:

@Service
public class SpringDataUserDetailsService implements UserDetailsService {

    @Autowired
    UserDao userDao;

    // 根据账号查询用户信息
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserDto userDto = userDao.getUserByUsername(username);
        if(userDto == null){
            // 如果用户查不到,返回null,由provider来抛出异常
            return null;
        }
        // 根据用户的id查询用户的权限
        List<String> permissions = userDao.findPermissionsByUserId(userDto.getId());
        //将permissions转成数组
        String[] permissionArray = new String[permissions.size()];
        permissions.toArray(permissionArray);
        UserDetails userDetails = User.withUsername(userDto.getUsername()).password(userDto.getPassword()).authorities(permissionArray).build();
        return userDetails;
    }
}

当用户请求时,Security便会拦截请求,取出其中的username字段,从Service中查询该账户,并将信息填充到一个userDetails对象中返回。这样Security便拿到了填充后的userDetails对象,也就是获取到了包括权限在内的用户信息。

验证

Security默认开启session。
配置好之后,当用户请求时,会要求先进行登录。若登陆成功,就会返回给用户一个session。
之后当用户访问接口时,每次都会对请求进行拦截过滤,取出session进行查询判断,验证是否具有接口的访问权限。
若有权限,则返回正常结果;否则返回403错误。

退出登录

由于使用session,因此退出登录需要将请求中的session清除:

@RequestMapping("/logout")
public String logout(HttpSession session){
    session.invalidate();
    return "退出成功";
}

然后设置httpSecurity.logout().logoutUrl("/logout"),注意logoutUrl需要设置为自定义的接口。

WebSecurityConfigurerAdapter.configure(HttpSecurity httpSecurity)相关

配置文件中,configure(HttpSecurity httpSecurity)用于定义一系列的过滤规则。

顺序优先级

整个http.xxx的校验是按照配置的顺序从上往下进行。若某个条件已匹配过,则后续同条件的匹配将不再被执行。也就是说,若多条规则是相互矛盾的,则只有第一条规则生效。典型地:

// 只有具有权限a的用户才能访问/get/resource
httpSecurity.authorizeRequests()
    .antMatchers("/get/resource").hasAuthority("a")
    .anyRequest().permitAll();

// 所有用户都能访问/get/resource
httpSecurity.authorizeRequests()
    .anyRequest().permitAll()
    .antMatchers("/get/resource").hasAuthority("a");

但若是包含关系,则需要将细粒度的规则放在前面:

// /get/resource需要权限a,除此之外所有的/get/**都可以随意访问
httpSecurity.authorizeRequests()
    .antMatchers("/get/resource").hasAuthority("a")
    .antMatchers("/get/**").permitAll();

授权方式

授权有两种方式:

  • web授权(基于请求url),在configure(HttpSecurity httpSecurity)中设置。
  • 方法授权(在方法上使用注解标明授权)。

web授权

@Override
httpSecurity.authorizeRequests() // 对请求进行授权
    .antMatchers("/test1").hasAuthority("p1") // 设置/test1接口需要p1权限
    .antMatchers("/test2").hasAnyRole("admin", "manager") // 设置/test2接口需要admin或manager角色
    .and() // 上一项配置已经结束,下一项配置将开始
    .httpBasic(); // 开启httpBasic登录

其中:

  • hasAuthority("p1")表示需要p1权限
  • hasAnyAuthority("p1", "p2")表示p1或p2权限皆可

同理:

  • hasRole("p1")表示需要p1权限
  • hasAnyRole("p1", "p2")表示p1或p2权限皆可

方法授权

@PreAuthorize("hasAuthority('p')")// 拥有p权限才可以访问
public String getResource(){
    return "访问资源";
}

在其中可以使用表达式:

@PreAuthorize("#user.name.equals('admin')")
public void getResource(User user) {
   System.out.println("只有管理员才能获取到的数据");
}

也可以基于角色:

@PreAuthorize("hasAnyRole('admin', 'manager')")
public void getResource(User user) {
   System.out.println("只有管理者才能获取到的数据");
}

表单登录

访问接口时,默认会打开一个登录页面,这就是表单登录。

httpSecurity.authorizeRequests()
	.and()
	.formLogin()//允许表单登录
	.successForwardUrl("/login-success");//自定义登录成功的页面地址

其中formLogin()表示允许表单登录。当调用了该方法,即会显示登录页面。
表单登录默认开启。若重载configure(HttpSecurity httpSecurity)且在其中没有调用formLogin(),则表单登录会关闭。此时可以启用httpBasic:

@Override
httpSecurity.httpBasic(); // 开启httpBasic登录

这样浏览器访问时会弹窗提示输入用户名和密码。
若不需要表单登录,那么不附加formLogin()httpBasic()

登出

httpSecurity.logoutUrl()httpSecurity.addLogoutHandler()是冲突的,通常只用其中之一。
对于使用token且服务端不进行存储的情况,不需要请求服务端进行登出,直接由前端将token丢弃即可。

跨域

要解决跨域问题,通常需要:

httpSecurity.cors()
	.and()
	.csrf().disable();

其中:

  • cors()允许跨域资源访问。
  • csrf().disable()禁用跨域安全验证。

session创建规则

httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED):指的是设置session的创建策略。包含4个值:

  • ALWAYS:总是使用session。若没有session则创建。
  • NEVER:不创建session。但若应用中有其他地方创建了session,将使用。即用户登录时将不创建session。
  • IF_REQUIRED:默认值。如果有必要,则创建session。也就是用户登录成功时为其创建一个session。
  • STATELESS:不创建session,也不使用session。

Security默认使用的的策略为IF_REQUIRED。若使用token的方式,意味着不需要session,那么就需要设为STATELESS

自定义过滤器

可基于Security提供的抽象类Filter创建自定义Filter,然后调用HttpSecurity的成员方法来添加到filterChain中,例如:
定义Filter:

public class MyFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws IOException, ServletException {
		filterChain.doFilter(httpServletRequest, httpServletResponse);
	}
}

然后添加到filterChain:

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    // 自定义token过滤器
    httpSecurity.addFilterBefore(new MyFilter(), UsernamePasswordAuthenticationFilter.class)
}

添加的方法除了addFilterBefore(),还有addFilter()addFilterAfter()addFilterAt()等。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值