Spring Security (一):Spring Security初体验

228 阅读7分钟

前言

在web应用开发中,安全无疑是十分重要的,选择Spring Security来保护web应用是一个非常好的选择。Spring Security 是spring项目之中的一个安全模块,可以非常方便与spring项目无缝集成。特别是在spring boot项目中加入spring security更是十分简单。本篇我们介绍spring security,以及spring security在web应用中的使用。今天就开始我们的第一小节 Spring Security的入门使用吧!

闲话就不说了,直接进入正题

创建不受保护的应用

假设我们现在创建好了一个springboot的web应用,有一个控制器如下:

@Controller
@RequestMapping("/sa")
public class HelloContoller {

 @RequestMapping("/hello")
 @ResponseBody
 public String test(){
    return "Hello ,spring security!";
 }
 
}

我们可以看到,这是一个简单的SpringBoot项目。那我们启动应用,假设端口是8080,那么当我们在浏览器访问http://localhost:8080/sa/hello 可以在浏览器看到 Hello ,spring security!。

我们需要明白。这个请求是任何人都可以访问的。接下来。我们加入spring security 来保护应用

我们在pom文件中加入Spring Security的依赖

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

再次访问http://localhost:8080/sa/hello时会让我们登入

这个页面是spring security 提供的默认的登录页面。让我们来登入下。默认的用户名是user,密码的话相信细心的同学会发现启动应用时控制台会打印一串很奇怪的字符,没错,那就是密码

输入用户名和密码后,是不是就可以看到控制器返回给我们的字符串了。是不是很有意思!

我们也可以自定义用户名和密码,不使用Spring Security给我们提供的。我们在application.properties文件中添加以下代码

spring.security.user.name=admin
spring.security.user.password=admin

在这里我自定义了用户名和密码都为admin的用户,重启项目,你会发现控制台并没有为我们提供随机密码。这时你再用你设置的用户名和密码登入会发现也能给你返回消息

角色-资源 访问控制

通常情况下,我们需要实现“特定资源只能由特定角色访问”的功能。假设我们的系统有如下两个角色:

ADMIN 可以访问所有资源

USER 只能访问特定资源

现在我们给系统增加“/product” 代表商品信息方面的资源(USER可以访问);增加”/admin”代码管理员方面的资源(USER不能访问)。代码如下:

//admin可以访问的资源
@Controller
@RequestMapping("/admin")
public class AdminController {
    @RequestMapping("/info")
    @ResponseBody
    public String info(){
        return "欢迎来到管理员页面,此处你是管理员";
    }
}
//user可以访问的资源
@Controller
@RequestMapping("/product")
public class ProductController {
    @RequestMapping("/info")
    @ResponseBody
    public String info(){
        return "欢迎返回供应商页面,此处你是普通用户";
    }
}

在正式的应用中,我们的用户和角色是保存在数据库中的;本例为了方便演示,我们来创建两个存放于内存的用户和角色。我们创建SecurityConfiguration配置类增加角色用户,这里类继承WebSecurityConfigurerAdapter 类,在这里我们重写它的configure方法。如下代码:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    /*
    在正式应用中,我们的用户和角色都是保存在数据库中
     本示例为了方便演示。我们在内存中创建用户和角色
     */


    //创建用户User和Admin
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.inMemoryAuthentication()
                .withUser("yyw")  //管理员 具有admin和user权限,可以访问所有资源
                .password("123456")         //管理员密码
                .roles("ADMIN","USER")      //设置admin和user权限
                .and()
                .withUser("zs")  //普通用户 只能访问 /product/**
                .password("123456")         //普通用户密码
                .roles("USER");             //设置user权限
    }


    //创建角色及权限
    protected void configure(HttpSecurity httpSecurity) throws Exception{
        httpSecurity.authorizeRequests()
                    .antMatchers("/product/**").hasRole("USER")  //为user角色分配可以访问的资源
                    .antMatchers("/admin/**").hasRole("ADMIN") //为admin角色分配可以访问的资源
                    .anyRequest().authenticated()
                    .and()
                    .formLogin().and()
                    .httpBasic();
    }
}

需要注意的是因为Spring boot 2.0.3引用的security 依赖是 spring security 5.X版本,此版本需要提供一个PasswordEncorder的实例,否则后台会报错误。

因此,需要创建PasswordEncorder的实现类。在这里我们定义一个MyPasswordEncoder

import org.springframework.security.crypto.password.PasswordEncoder;

public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence charSequence) {
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return s.equals(charSequence.toString());
    }
}

然后,我们在我们上面定义的SecurityConfiguration类configure方法中加上这么一句代码

protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())   //加上这句
                .withUser("yyw")  //管理员 具有admin和user权限,可以访问所有资源
                .password("123456")         //管理员密码
                .roles("ADMIN","USER")      //设置admin和user权限
                .and()
                .withUser("zs")  //普通用户 只能访问 /product/**
                .password("123456")         //普通用户密码
                .roles("USER");             //设置user权限
    }

下面来验证一下普通用户登录,重启项目,在浏览器中输入http://localhost:8080/admin/info。同样,我们会到达登录页面,我们输入用户名zs,密码也为123456 结果错误页面了,拒绝访问了,信息为:

There was an unexpected error (type=Forbidden, status=403).
Access is denied

我们把浏览器中的uri修改成:/product/info,结果访问成功。可以看到 欢迎返回供应商页面,此处你是普通用户。说明 zs只能访问 product/**,这个结果与我们预期一致。

再来验证一下管理员用户登录,重启浏览器之后,输入http://localhost:8080/admin/info。在登录页面中输入用户名yyw,密码123456,提交之后,可以看到信息,说明可以访问管理员资源了。我们再将浏览器uri修改成/product/info,刷新之后,也能看到打印出来的信息 ,说明 yyw用户可以访问所有资源,这个也和我们的预期一致。

让我们回顾下,在HelloContoller 中的sa/hello 我们是没有添加至角色及权限中。那让我们用admin的身份去访问 sa/hello 会发现也可以返回信息,再让我们切换回user身份发现也可以返回信息。由此我们得出一个结论

只要请求路径没有配置在以下的方法中,那么谁都可以访问。下面我们来验证下。我们将sa/**的请求配置在ADMIN角色下面

protected void configure(HttpSecurity httpSecurity) throws Exception{
        httpSecurity.authorizeRequests()
                    .antMatchers("/product/**").hasRole("USER")  //为user角色分配可以访问的资源
                    .antMatchers("/admin/**","/sa/**").hasRole("ADMIN") //为admin角色分配可以访问的资源
                    .anyRequest().authenticated()
                    .and()
                    .formLogin().and()
                    .httpBasic();
    }

我们再次重新启动项目。会发现只有admin的身份才可以访问了刚刚那个hello请求了

我们还可以获取当前登录的用户信息

上面我们实现了“资源 – 角色”的访问控制,效果和我们预期的一致,但是并不直观,我们不妨尝试在控制器中获取“当前登录用户”的信息,直接输出,看看效果。修改 sa/hello请求的方法

@RequestMapping("/hello")
    public String test(){
        String currentUser = "";
        //我们通过SecurityContextHolder来获取了用户信息
        Object principl = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if(principl instanceof UserDetails) {
            currentUser = ((UserDetails)principl).getUsername();
        }else {
            currentUser = principl.toString();
        }
        return " some sa hello,currentUser is: "+currentUser;
    }

启动项目,让我们再次访问hello请求,我们会发现页面输出了用户信息

小结

至此,我们已经对spring security有了一个基本的认识了。了解了如何在项目中加入spring security,以及如何控制资源的角色访问控制。spring security原不止这么简单,我们才刚刚开始。为了能够更好的在实战中使用spring security 我们需要更深入的了解。好了,今天就到这吧。我们下期再见!

非特殊说明,本文版权归 在代码里游泳 所有,转载请注明出处. 如果文章有不足的地方,欢迎提点,后续会完善。

如果文章对您有帮助,请给我点个赞。谢谢!