其他分享
首页 > 其他分享> > SpringSecurity学习笔记

SpringSecurity学习笔记

作者:互联网

1、SpringSecurity学习笔记

SpringSecurity底层本质是一个过滤器链

 

FilterSecurityInterceptor:是一个方法级的过滤器,位于过滤器链的最底部

ExceptionTranslationFilter:异常过滤器,用来处理认证授权过程中抛出的异常

UsernamePasswordAuthenticationFilter:对/login的POST请求做拦截,校验表单中的用户名和密码。

 

1、两个重要接口

1、UserDetailsService:从数据库查询用户名和密码的过程

 

2、PasswordEncoder:数据加密的接口,一般用于返回User对象密码的加密

 

2、认证

1、设置登录用户名和密码

 

2、通过数据库完成用户认证

1、mybatis-plus依赖

 <!--Mybatis-Plus-->
 <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
 </dependency>

2、实体类Users.java

 import lombok.Data;
 ​
 import java.io.Serializable;
 ​
 @Data
 public class Users implements Serializable {
     private Integer id;
 ​
     private String username;
 ​
     private String password;
 }
 ​

3、数据库接口UsersMapper.java

 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.zq.security.pojo.Users;
 ​
 public interface UsersMapper extends BaseMapper<Users> {
 }

4、自定义UserDetailsService

 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.zq.security.mapper.UsersMapper;
 import com.zq.security.pojo.Users;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Service;
 ​
 import java.util.List;
 ​
 @Service
 public class MyUserDetailsService implements UserDetailsService {
 ​
     @Autowired
     private UsersMapper usersMapper;
 ​
     @Autowired
     private PasswordEncoder encoder;
 ​
     @Override
     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
         // 根据表单传过来的用户名从数据库查询用户信息
         QueryWrapper<Users> queryWrapper = new QueryWrapper<>();
         queryWrapper.eq("username",username);
         Users users = usersMapper.selectOne(queryWrapper);
 ​
         // 当用户名不存在时抛出异常
         if (null == users){
             throw new UsernameNotFoundException("用户名不存在");
        }
 ​
         List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
         // 将从数据库查询的用户名和密码封装到User并进行返回
         return new User(users.getUsername(),encoder.encode(users.getPassword()),authorities);
    }
 }

5、config配置类

 import org.springframework.beans.factory.annotation.Autowired;
 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.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
 ​
 @Configuration
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 ​
     @Autowired
     private UserDetailsService userDetailsService;
 ​
     @Override
     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
         auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }
 ​
     @Bean
     public PasswordEncoder passwordEncoder(){
         return new BCryptPasswordEncoder();
    }
 }

 

3、自定义登录页面和白名单

1、config配置类

 @Configuration
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 ​
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         // 自定义自己编写登录页面
         http.formLogin()
                 // 登录页面的路径
                .loginPage("/login.html")
                 // 点击登录访问的路径
                .loginProcessingUrl("/user/login")
                 // 登录成功后跳转的路径
                .defaultSuccessUrl("/user/index").permitAll()
                 // 哪些访问路径不需要认证可以直接访问
                .and().authorizeRequests()
                .antMatchers("/hello","/user/login").permitAll()
                 // 任何路径都能访问
                .anyRequest().authenticated()
                 // 关闭csrf防护
                .and().csrf().disable();
    }
 }
 ​

 

3、授权

1、hasAuthority方法:当前用户具有指定的权限,有则返回true,否则返回false

注意:此方法只能针对一个权限

 

2、hasAnyAuthority方法:当前用户具有指定权限的任意一种则返回true,否则返回false

 

3、hasRole方法:当前用户具有指定角色才能允许访问

 

4、hasAnyRole方法:拥有任意一个角色才能访问

4、自定义403没有权限访问页面

1、配置类中配置

 @Configuration
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 ​
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         // 没有权限时跳转的页面路径
         http.exceptionHandling().accessDeniedPage("/unauth.html");
 }

 

5、常用注解的使用

1、@Secured("ROLE_role1,ROLE_role1")

指定角色才能访问,使用在Controller的方法上

注意:使用该注解必须在主启动类上添加@EnableGlobalMethodSecurity(securedEnabled = true)

 @GetMapping("/update")
 @Secured({"ROLE_update","ROLE_sale"})
 public String update(){
    return "hello update";
 }

 

2、@PreAuthorize("hasAnyAuthority('menu:system')")

注解进入方法前的权限验证,@PreAuthorize可以将登录用户的角色或权限传入方法中

注意:使用该注解必须在主启动类上添加@EnableGlobalMethodSecurity(prePostEnabled = true)

  @GetMapping("/insert")
 // @PreAuthorize("hasRole('ROLE_管理员')")
 @PreAuthorize("hasAnyAuthority('menu:system')")
 public String insert(){
   return "hello insert";
 }

 

3、@PostAuthorize("hasAnyAuthority('menu:system')")

这个注解用的并不多,在方法执行后进行权限验证,适合验证带有返回值权限

注意:使用该注解必须在主启动类上添加@EnableGlobalMethodSecurity(prePostEnabled = true)

 @GetMapping("/select")
 @PostAuthorize("hasAnyAuthority('menu:normal')")
 public String select(){
   System.out.println("hello select");
   return "hello select";
 }

 

4、@PostFilter("filterObject.username == 'admin1'")

权限验证之后对返回的数据进行过滤,留下的是admin1的数据

表达式中filterObject引用的是方法返回值List中的某一个元素

 @GetMapping("/list")
 @PostFilter("filterObject.username == 'admin1'")
 @PreAuthorize("hasRole('ROLE_管理员')")
 public List<Users> list(){
    List<Users> users = Arrays.asList(
            new Users(1,"admin1","123"),
            new Users(2,"admin2","456"),
            new Users(1,"admin2","789"),
            new Users(1,"admin1","321"),
            new Users(1,"admin3","135")
    );
    return users;
 }

 

5、@PreFilter("filterObject.id % 2 == 0")

权限验证之前对传入的数据进行过滤,当id是2的倍数的时候则留下

 @GetMapping("/list2")
 @PreFilter("filterObject.id % 2 == 0")
 @PreAuthorize("hasRole('ROLE_sale')")
 public List<Users> list2(List<Users> users){
    return users;
 }

 

6、用户注销

1、配置类中配置

 @Configuration
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 ​
 @Override
 protected void configure(HttpSecurity http) throws Exception {
     // 用户注销
     http.logout()
          // 用户点注销访问的url
        .logoutUrl("/logout")
          // 注销成功跳转的url
        .logoutSuccessUrl("/user/logout")
        .permitAll();
 }

 

7、基于数据库实现“记住我”

1、原理:

浏览器端:Cookie 储存 加密串

数据库端:加密串 与 用户信息字符串对应

认证时:Cookie 使用 加密串匹配用户信息进行认证

1、第一步:创建数据库表

 CREATE TABLE persistent_logins (
  username VARCHAR ( 64 ) NOT NULL,
  series VARCHAR ( 64 ) PRIMARY KEY,
 token VARCHAR ( 64 ) NOT NULL,
 last_used TIMESTAMP NOT NULL)

 

2、配置类中进行配置:注入数据源,配置操作数据库对象

 @Configuration
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 ​
     /**
      * 注入数据源
      */
     @Autowired
     private DataSource dataSource;
 ​
     /**
      * 将数据库操作对象交由spring容器管理
      * @return
      */
     @Bean
     public PersistentTokenRepository persistentTokenRepository(){
         JdbcTokenRepositoryImpl bean = new JdbcTokenRepositoryImpl();
         bean.setDataSource(dataSource);
         // 自动生成表
         // bean.setCreateTableOnStartup(true);
         return bean;
    }
 ​
 }

 

3、配置类进一步配置,设置记住我功能

关键代码:

// 记住我有效时长,以秒为单位 .tokenValiditySeconds(60) .userDetailsService(userDetailsService) // 关闭csrf防护 .and().csrf().disable();

 @Configuration
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 ​
     @Autowired
     private UserDetailsService userDetailsService;
 ​
     /**
      * 注入数据源
      */
     @Autowired
     private DataSource dataSource;
 ​
     /**
      * 将数据库操作对象交由spring容器管理
      * @return
      */
     @Bean
     public PersistentTokenRepository persistentTokenRepository(){
         JdbcTokenRepositoryImpl bean = new JdbcTokenRepositoryImpl();
         bean.setDataSource(dataSource);
         // 自动生成表
         // bean.setCreateTableOnStartup(true);
         return bean;
    }
     
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         // 自定义自己编写登录页面
         http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/user/login")
                .defaultSuccessUrl("/user/index").permitAll()
                 // 开启记住我
                .and().rememberMe().tokenRepository(persistentTokenRepository())
                 // 记住我有效时长,以秒为单位
                .tokenValiditySeconds(60)
                .userDetailsService(userDetailsService)
                 // 关闭csrf防护
                .and().csrf().disable();
    }
 }

 

4、登录页面添加=="记住我"==复选框

  记住我:<input type="checkbox" name="remember-me" title="记住我" />

注意:name必须为remember-me

 

8、CSRF理解

什么是CSRF?

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

 

9、微服务权限案例

 

标签:springframework,SpringSecurity,学习,笔记,org,import,security,new,public
来源: https://www.cnblogs.com/zhouqiangshuo/p/16511969.html