springsecurity简单学习

一、初识SpringSecurity

在springboot项目中加入spring security。

1、在pom.xml中加入依赖

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

2、启动

在启动信息里面有一个自动生成的密码,默认用户名是user。

2018-10-18 15:31:10.241  INFO 4996 --- [           main] .s.s.UserDetailsServiceAutoConfiguration : 

Using generated security password: dd6f7b68-7409-4195-b908-b78cc9ec92af

此时我们就可以用user:dd6f7b68-7409-4195-b908-b78cc9ec92af登录了。

3、自己配置用户名密码

在application.yml中加入自定义配置

spring:
  security:
    user:
      name: admin
      password: admin

此时用户名密码就被定义成admin:admin了。

4、自定义配置类

更进一步,上面的安全模式只能有一个用户名密码,我们写一个自定义的配置类,配置多个用户名密码。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
            .withUser("user")
            .password("user")
            .roles("USER")
            .and()
            .withUser("admin")
            .password("admin")
            .roles("ADMIN")
            .and()
            .withUser("zhangsan")
            .password("123456")
            .roles("ADMIN");
    }
    
    @Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }
}
@EnableWebSecurity注解开启Spring Security的功能
@EnableGlobalMethodSecurity(prePostEnabled = true)这个注解,可以开启security的注解,我们可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解

在configure(HttpSecurity http)方法里面,默认的认证代码是:

http
    .authorizeRequests()
    .anyRequest().authenticated()
    .and()
    .formLogin()
    .and()
    .httpBasic();

NoOpPasswordEncoder:springboot默认对密码会解析,加上这个是保持用户输入的密码

5、权限的简单使用

我们可以在方法上加上权限注解,如下:

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {

    @RequestMapping("/home")
    @PreAuthorize("hasRole('ADMIN')")
    public  String  home() {
        return "hello world";
    }
}

常用权限

表达式 描述
hasRole([role]) 当前用户是否拥有指定角色。
hasAnyRole([role1,role2]) 多个角色是一个以逗号进行分隔的字符串。如果当前用户拥有指定角色中的任意一个则返回true。
hasAuthority([auth]) 等同于hasRole
hasAnyAuthority([auth1,auth2]) 等同于hasAnyRole
Principle 代表当前用户的principle对象
authentication 直接从SecurityContext获取的当前Authentication对象
permitAll 总是返回true,表示允许所有的
denyAll 总是返回false,表示拒绝所有的
isAnonymous() 当前用户是否是一个匿名用户
isRememberMe() 表示当前用户是否是通过Remember-Me自动登录的
isAuthenticated() 表示当前用户是否已经登录认证成功了。
isFullyAuthenticated() 如果当前用户既不是一个匿名用户,同时又不是通过Remember-Me自动登录的,则返回true。

二、进阶 Security

用数据库存储用户和角色,实现安全认证。

1、先定义一个user:

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;

public class User implements UserDetails {
    private String username;
    private String password;
    private Collection<? extends GrantedAuthority> roles;

    public Collection<? extends GrantedAuthority> getRoles() {
        return roles;
    }
    public void setRoles(Collection<? extends GrantedAuthority> roles) {
        this.roles = roles;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles;
    }
    @Override
    public String getPassword() {
        return password;
    }
    @Override
    public String getUsername() {
        return username;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}

2、定义一个service:

import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.ArrayList;
import java.util.List;

public class MyUserDetailsServiceimp implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        List<SimpleGrantedAuthority> list = new ArrayList<>();
        list.add(new SimpleGrantedAuthority("ROLE_ADMIN")); 
        // ROLE默认是带ROLE_开头
        User user = new User();
        user.setUsername(s);
        user.setPassword(s);
        user.setRoles(list);
        return user;
    }
}
我们只定义了用户名,密码,角色列表,
其他的账号过期,账号上锁,凭证过期,是否可用都默认设置为true,暂时不考虑。
实际使用的时候是根据username去数据库查询记录,
在根据的userid查询role,
并根据记录设置相关的账号过期,上锁等情况。

3、修改我们的配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(getUserDetailService());
    }

    @Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }

    @Bean
    public UserDetailsService getUserDetailService() {
        return new MyUserDetailsServiceimp();
    }
}

用户密码角色的获取从UserDetailService获取。

4、不拦截的页面

有些页面我们不想拦截,所有人都能访问,这时候只需要修改我们的配置类就行了:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/plugins/**",
                        "/css/**",
                        "/fonts/**",
                        "/js/**",
                        "/home1",
                        "/home2"
                ).permitAll() //默认不拦截静态资源的url pattern (2)
                .anyRequest().authenticated().and()
                .formLogin()
                //.loginPage("/login")// 登录url请求路径 (3)
                //.defaultSuccessUrl("/httpapi").permitAll()
                .and() // 登录成功跳转路径url(4)
                .logout().permitAll();

        http.logout().logoutSuccessUrl("/"); // 退出默认跳转页面 (5)
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(getUserDetailService());
    }

    @Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }

    @Bean
    public UserDetailsService getUserDetailService() {
        return new MyUserDetailsServiceimp();
    }
}
如上所示,css,js,font不拦截,/根不拦截,/home1, /home2不拦截
还可以自定义登录页面,以及登录成功之后的跳转页面

我们看一下此时的homecontroller

@RestController
public class HomeController {
    @RequestMapping("/")
    public  String  index() {
        return "hello index!";
    }
    @RequestMapping("/home")
    public  String  home() {
        return "hello home";
    }
    @RequestMapping("/home1")
    public  String  home1() {
        return "hello home1";
    }
    @RequestMapping("/home2")
    public  String  home2() {
        return "hello home2";
    }
}

5、session

spring security默认帮我们管理session,我们改下controller,如下:

@RestController
public class HomeController {

    @RequestMapping("/")
    public  String  index() {
        return "hello index!";
    }

    @RequestMapping("/home")
    @PreAuthorize("hasRole('ADMIN')")
    public  String  home() {
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String username = "";
        if (principal instanceof UserDetails) {
            username = ((UserDetails)principal).getUsername();
        } else {
            username = principal.toString();
        }
        System.out.println(username);
        return "hello home";
    }

    @RequestMapping("/home1")
    public  String  home1() {
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String username = "";
        if (principal instanceof UserDetails) {
            username = ((UserDetails)principal).getUsername();
        } else {
            username = principal.toString();
        }
        System.out.println(username);
        return "hello home1";
    }
}

启动服务,我们一次访问:

http://localhost:8080/home1
http://localhost:8080/home
http://localhost:8080/home1

控制台打印如下:

anonymousUser
zhangsan
zhangsan
原文地址:https://www.cnblogs.com/zhangbin1989/p/9812014.html