1.Spring Boot Security简介

Spring Security是一個安全组件。Spring Security采用“安全层”概念使每一层都尽可能安全。Spring Security可以在Controller层、service层、Dao层等通过加注解的方式来保护应用程序的安全。

在安全方面,有两个主要的领域:认证和授权。认证是指认证主体的过程,通常是指可以在应用程序中执行操作的用户、设备或其他系统;授权是指决定是否允许已认证的主题执行某一操作

Spring Security框架中,主要包含2个依赖jar:spring-security-web和spring-security-config.而spring boot security时对spring security框架做了封装,并没有改动spring security。springboot security起步依赖:spring-boot-starter-security

2. Spring Boot Security实例介绍

2.1 配置Spring Boot Security

2.1.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.forezp</groupId>
    <artifactId>springboot-security</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>springboot-security</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
        </dependency>

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

        <!--  sql-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>
View Code

 

2.1.2 配置Spring Security

新建SecurityConfig配置类,它继承WebSecurityConfigurerAdapter。在SecurityConfig类上加@EnableWebSecurity注解,开启websecurity功能

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                    .antMatchers("/css/**", "/index").permitAll()
                    .antMatchers("/user/**").hasRole("USER")
                    .antMatchers("/blogs/**").hasRole("USER")
                    .and()
                .formLogin().loginPage("/login").failureUrl("/login-error")
                .and()
                .exceptionHandling().accessDeniedPage("/401");
        http.logout().logoutSuccessUrl("/");
    }
    @Autowired
    UserDetailsService userDetailsService;
//
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }


    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); // 在内存中存放用户信息
        manager.createUser(User.withUsername("forezp").password("123456").roles("USER").build());
        manager.createUser(User.withUsername("admin").password("123456").roles("ADMIN").build());
        return manager;
    }
}
View Code

 

 

上述代码中,

configureGlobal()配置内容如下:

通过AuthenticationManagerBuuilder在内存中创建一个认证用户的信息,该认证用户有两个角色:USER角色(用户名forezp,密码123455),ADMIN角色(用户名为admin密码为123456)

重写的configure()配置如下内容:

a) 以/css/**开头资源和/index资源不需要账户认证,外界请求可以直接访问这些资源

b)  以/user/** 和/blogs/**开头的资源需要验证,并且需要USER角色才可以登录(即用户名forezp,密码123456)

c) 表单登录地址是/login,登陆失败的地址是login-error

d) 异常处理会重定向到"401"界面

e) 注销登录成功,重定向到首页

2.2 Spring Security方法级别上保护

Spring Security从2.0版本开始,提供方法级别的安全支持。Security Config配置类加上@EnableWebSecurity 和@EnableGlobalMethodSecurity开启方法级别保护,代码如下:

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public SecurityConfig extends WebSecurityConfigurerAdapter{...}

 

EnbleGlobalMethodSecurity括号中参数如下:

  prePostEnabled: Spring Security的Pre和Post注解是否可用,即具体权限接口上@PreAuthorize和@PostAuthorize是否可用;一般来说@PreAuthorize用的多(进入方法前进行权限验证)

  secureEnabled: Spring Security的@Secured注解是否可用

  jsr250Enabled: Spring Security对JSR-250的注解是否可用

在方法上写权限注解:

比如某个方法有ADMIN权限,则可以在方法上写为@PreAuthorize("hasRole('ADMIN')")或者@PreAuthorize("hasAuthority('ROLE_ADMIN')");

添加多个权限点:@PreAuthorize("hasAnyRole('ADMIN',‘USER')")或者@PreAuthorize("hasAnyAuthority('ROLE_ADMIN','ROLE_USER')")

实例:

两个接口:query() USER权限即可访问,delete() ADMIN权限才可以

@RestController
@RequestMapping("/blogs")
public class BlogController {

    @Autowired
    BlogService blogService;
    @GetMapping
    public ModelAndView query(Model model) {

        List<Blog> list =blogService.getBlogs();
        model.addAttribute("blogsList", list);
        return new ModelAndView("blogs/list", "blogModel", model);
    }

    @PreAuthorize("hasAuthority('ROLE_ADMIN')")  //
    @GetMapping(value = "/{id}/deletion")
    public ModelAndView delete(@PathVariable("id") Long id, Model model) {
        blogService.deleteBlog(id);
        model.addAttribute("blogsList", blogService.getBlogs());
        return new ModelAndView("blogs/list", "blogModel", model);
    }
}

 

另外在Security Config中赋登录账号admin以USER和和ADMIN权限

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); // 在内存中存放用户信息
        manager.createUser(User.withUsername("forezp").password("123456").roles("USER").build());
        manager.createUser(User.withUsername("admin").password("123456").roles("ADMIN","USER").build());
        return manager;
    }
}

 

程序启动后,当使用forezp作为账号登录后执行delete操作时,返回如下

 

 当使用admin作为账号登陆后执行delete操作时,可以把相应内容删除

2.3 从数据库中读取用户的认证信息

2.1和2.2节中都是采用从内存(代码hardcode)中配置了用户信息,包括用户名、密码以及用户的角色权限信息,但是当用户数量非常多时,这种方式是不可取的。所以需要将从db中读取用户名、密码以及用户角色权限信息。

创建User实体类

注意该实体类需要实现UserDetail(UserDetail接口是springsecurity的一个接口)

UserDetail接口它是实现Spring Security认证信息的核心接口。其中getUsername()为UserDetail接口方法,这个接口不一定返回username,也可以是用户信息(手机号、身份证号等);getAuthorities()返回的是用户设置的权限信息

@Entity
public class User implements UserDetails, Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false,  unique = true)
    private String username;

    @Column
    private String password;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
    private List<Role> authorities;

    public User() {
    }

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    public void setAuthorities(List<Role> authorities) {
        this.authorities = authorities;
    }

    @Override
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }


}
View Code

 

创建Role实体类

Role类实现GantedAuthority接口,并重写了其getAuthority()

创建Service层

Service层需要实现userDetailService接口(userDetailService是spring security一个接口),该接口是根据用户名获取该用户的所有信息

@Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserDao userRepository;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findByUsername(username);
    }
}

 

修改SecurityConfig

修改SecurityConfig配置类,让Spring Security从db中获取用户的认证信息

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                    .antMatchers("/css/**", "/index").permitAll()
                    .antMatchers("/user/**").hasRole("USER")
                    .antMatchers("/blogs/**").hasRole("USER")
                    .and()
                .formLogin().loginPage("/login").failureUrl("/login-error")
                .and()
                .exceptionHandling().accessDeniedPage("/401");
        http.logout().logoutSuccessUrl("/");
    }
    @Autowired
    UserDetailsService userDetailsService;
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService); //这里的userDtailService在上面中有具体实现类UserService
    }
}

3. 小结

使用Spring Security首先引入Spring Security相关依赖;

然后写一个配置类,该配置类继承WebSecurityConfigurerAdapter,并在该配置类加上注解@EnableWebSecurity注解开启WebSecurity

然后在配置类中配置AuthenticationManagerBuilder,AuthenticationmangerBuilder配置了用户读取的认证信息方式,可以从内存也可以从db

然后配置HttpSecurity,HttpSecurity配置了请求认证规则,即那些url需要认证以及需要什么权限才能访问

最后如果需要开启方法级别的安全配置需要通过在配置类上加上@EnableGlobalMethodSecurity注解开启

 

 

具体代码:https://github.com/forezp/springcloud-book/tree/master/chapter13

 

posted on 2023-02-07 21:39  colorfulworld  阅读(182)  评论(0编辑  收藏  举报