栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringSecurity学习笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringSecurity学习笔记

文章目录
  • SpringSecurity的基本使用
    • 初始化项目
        • 新建springboot项目,选择springsecurity、web、mysql、mybatis依赖
        • 配置数据库(数据库不存在需手动创建)
        • 启动项目
        • 编写一个接口,并访问
    • 学习配置security
        • 1.自定义登录
        • 2.登录成功、登录失败处理
        • 3.密码加密
        • 4.自定义查询用户
          • 1.内存中
          • 2.数据库
        • 5.角色和权限
          • 权限
          • **角色**
          • IP控制
          • 自定义403权限不足提示
        • 6.注解
          • @Secured
          • @PreAuthorize()和@PostAuthorize()
        • 7.记住我
        • 8.退出登录
  • jwt+oauth2待更新

SpringSecurity的基本使用 重启项目后务必要刷新页面 初始化项目 新建springboot项目,选择springsecurity、web、mysql、mybatis依赖

            org.springframework.boot
            spring-boot-starter-security
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            mysql
            mysql-connector-java
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.2.0
        
配置数据库(数据库不存在需手动创建)
spring.datasource.password=111111
spring.datasource.username=root
spring.datasource.url=jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
启动项目

看到他,说明就没问题

默认用户名:user

默认密码:

当然现在登录成功是404,因为我们什么都没有写。

为什么我们会访问8080就是这个页面?

因为security默认是会要求所有请求认证。在我们没有被认证(即登录)时,任何请求默认都会重定向到这个登录页。

编写一个接口,并访问
@RestController
public class TestController {

    @GetMapping("/hello")
    public String hello() {
        return "hello world!";
    }
}

访问:localhost:8080/hello

依然要先登录,然后才能看到

学习配置security 1.自定义登录

前后端分离的话,可以看看这篇文章:https://blog.csdn.net/qq_42682745/article/details/121326021

我们的配置类需要继承WebSecurityConfigurerAdapter

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);
        http.formLogin()        //开启表单登录
                .loginPage("/login.html")   //定义登录页面
                .loginProcessingUrl("/mylogin")   //自定义登录请求,form表单中的action就是它
                .usernameParameter("myName")   //自定义用户名参数,默认表单中必须是username
                .passwordParameter("myPass");   //自定义密码参数,默认表单中必须是password


        http.authorizeRequests()    //认证相关
                .antMatchers("/login.html").permitAll()  //放行登录页面
                .antMatchers("/Main.html").denyAll()  //拒绝访问
                .anyRequest().authenticated();   //其余请求都必须认证后才能访问

        
        //从 Spring Security4 开始 CSRF 防护默认开启。默认会拦截请求。进行 CSRF 处理。
        // CSRF 为了保证不是其他第三方网站访问,
        // 要求访问时携带参数名为_csrf 值为 token(token 在服务端产生)的内容,如果token 和	服务端的 token 匹配成功,则正常访问。
        //所以关闭csrf请求才能成功
        http.csrf().disable();
    }
}
  • .antMatchers("/login.html").permitAll() 将matchers中的资源放行,不认证即可访问
  • .anyRequest().authenticated() 除去上面matchers中的资源,其余资源访问都需要认证

随便写一个登录页面:




    
    Title


    

ok,

现在我们访问:localhost:8080/hello , 被重定向到我们自定义的登陆页面了

,再输入user和那串密码,

成功

2.登录成功、登录失败处理

successForwardUrl("/success")  //登录成功的post处理接口
.failureForwardUrl("/unsuccess")  //登录失败的post处理接口

失败:

成功:

3.密码加密

PasswordEncoder核心接口,进入PasswordEncoder接口,鼠标双击类名,Ctrl+h,

可以看到,加密方式很多啊。

security官方推荐的是BCryptPasswordEncoder。

下面我们在测试类中用一下:使用BCryptPasswordEncoder加密123456

下面我们在测试类中用一下:使用NoOpPasswordEncoder加密123456

NoOpPasswordEncoder,就是不加密密码,只推荐测试使用;这个已经被标记为过时、不推荐使用。

4.自定义查询用户

UserDetailsService、UserDetails接口就是核心,我们需要了解。

进入UserDetailsService接口,鼠标双击类名,Ctrl+h,

我们就了解,内存中查询用户和数据库查询用户

再看看UserDetails接口,

它就是security的一个用户单位,我们使用它的实现类User即可。注意,是security的User不是你自己写的。

1.内存中

在刚刚的配置文件中,加上这两个方法(一个创建获取用户的bean,一个创建密码加密器bean):

	@Bean
    public UserDetailsService users() {
        //创建内存UserDetailsManager对象
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        //创建用户张三李四
        manager.createUser(User.withUsername("张三").password("123").authorities("p1","p2").build());
        manager.createUser(User.withUsername("李四").password("123").authorities("p3").build());
        return manager;
    }

    @Bean
    public PasswordEncoder encoder() {
        return NoOpPasswordEncoder.getInstance();
    }

张三,密码123,权限p1,p2

李四,密码123,权限p3

ok,现在我们试试用这两个账号登录

登录成功,下面我们再加上权限

http.authorizeRequests()    //认证相关        .antMatchers("/login.html").permitAll()  //放行登录页面        .antMatchers("/unsuccess").permitAll()        //访问hello请求需要有p1权限        .antMatchers("/hello").hasAnyAuthority("p1")        .anyRequest().authenticated();   //其余请求都必须认证后才能访问

先使用,张三(p1、p2权限),登录成功后,浏览器访问localhost:8080/hello

访问成功,因为张三有p1权限。

下面试试李四(p3),登录成功后,浏览器访问localhost:8080/hello

403,无权访问

2.数据库

就是写一个service实现UserDetailsService,loadUserByUsername方法即可。调用mapper去数据库查

@Service
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询数据库
        MyUser myUser = userMapper.findByName(username);
        if (myUser == null) {
            return null;
        }
        return new User(myUser.getName(),myUser.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(myUser.getAuth()));
    }
}

AuthorityUtils.commaSeparatedStringToAuthorityList(“p1,p2”),这个方法会将字符串以逗号分隔为list

这里图方便,我就伪造数据了。。。。用户名是xp,就返回一个User对象,密码123456,权限p1,p2

@Servicepublic class MyUserDetailsService implements UserDetailsService {    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        if ("xp".equals(username)) {            return new User(username,"123456", AuthorityUtils.commaSeparatedStringToAuthorityList("p1,p2"));        }        return null;    }}

重启,登录xp

登录成功,并且访问/hello也是可以的

5.角色和权限 权限

权限在前文已经涉及到了

.antMatchers("/hello").hasAnyAuthority("p1","p2") //有其中一个权限即可

相信大家很容易理解,有什么权限才能干什么事

角色

其实和权限差不多

角色,必须以ROLE_ 开头,后面跟角色名

@Servicepublic class MyUserDetailsService implements UserDetailsService {    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        if ("xp".equals(username)) {            return new User(username,"123456", AuthorityUtils.commaSeparatedStringToAuthorityList("p1,p2,ROLE_admin,ROLE_test"));        }        return null;    }}

我们再改一下security的配置,加上:.antMatchers("/test").hasAnyRole(“admin”,“test”)

在配置类中就只需要写角色名,不能加ROLE_

http.authorizeRequests()    //认证相关
        .antMatchers("/login.html").permitAll()  //放行登录页面
        .antMatchers("/unsuccess").permitAll()
        .antMatchers("/Main.html").denyAll()  //拒绝访问
        //访问hello请求需要有p1权限
        .antMatchers("/hello").hasAnyAuthority("p1","p2")
        .antMatchers("/test").hasAnyRole("admin","test")
        .anyRequest().authenticated();   //其余请求都必须认证后才能访问

访问/test接口需要有角色admin或者test,有其中一个角色即可访问。登录xp,访问test

IP控制
//指定ip才能访问
.antMatchers("/test").hasIpAddress("192.168.1.4")

localhost的ip是 0:0:0:0:0:0:0:1

127.0.0.1 的ip就是本身

自定义403权限不足提示

https://www.bilibili.com/video/BV1gb4y1b7XE?p=20

自己写一个类,实现AccessDeniedHandler接口

将MyAccessDeniedHandler注入到配置类,然后

6.注解 @Secured

作用于类、方法

验证是否有具有指定角色,有才能访问

角色必须是以ROLE_ 开头的


现在,将配置类的角色相关的注释掉

1.启动类,增加@EnableGlobalMethodSecurity(securedEnabled = true)

2.在test接口上加上

@Secured("ROLE_admin,ROLE_test")@GetMapping("/test")public String test() {    return "您可以访问/test接口";}

重启,登录访问test,成功。

现在将test接口的角色,改一下

@Secured("ROLE_xx")@GetMapping("/test")public String test() {    return "您可以访问/test接口";}

无权访问

值得一提的是,@Secured(“ROLE_admin,ROLE_test”)和配置中的hasAnyRole(“admin”,“test”)。虽然都能写多个角色。hasAnyRole(“admin”,“test”)只需要其中一个权限就能访问。但是@Secured(“ROLE_admin,ROLE_test”)必须全部具备才能访问

@PreAuthorize()和@PostAuthorize()

它两也作用于方法或者类。

判断有不有权限

@PreAuthorize(),执行方法前判断

@PostAuthorize(),执行方法后判断

很明显,我们既然做权限控制,自然是使用@PreAuthorize()更多,它两的用法一样。

1.启动类注解:

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)

2.修改test接口

//@Secured("ROLE_admin,ROLE_test")
@PreAuthorize("hasAnyAuthority('p1','p2')")
@GetMapping("/test")
public String test() {
    return "您可以访问/test接口";
}

3.将配置类中权限控制相关的注释调

启动–登录–访问test–成功

修改test接口:

@PreAuthorize("hasAnyAuthority('p8')")
@GetMapping("/test")
public String test() {
    return "您可以访问/test接口";
}

@PreAuthorize里面的写法和配置文件一样,接口都是有提示的

7.记住我

我们需要在配置类中加上一些东西:

1.注入依赖:

@Autowired
private MyUserDetailsService myUserDetailsService;
@Autowired
private DataSource dataSource; //配置好mysql参数后,springboot会自动装配一个DataSource
@Autowired
private PersistentTokenRepository persistentTokenRepository;

2.写bean方法:

@Bean
//返回持久化token到数据库的bean
public PersistentTokenRepository persistentTokenRepository() {
    JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
    //配置数据源
    jdbcTokenRepository.setDataSource(dataSource);
    //自动创建所需要的表,第二次启动需要注释掉不然报错
    jdbcTokenRepository.setCreateTableonStartup(true);
    return jdbcTokenRepository;
}

3.加上配置

http.rememberMe()  //开启记住我功能
        .rememberMeParameter("remember")  //自定义记住我参数名,remember-me
        .userDetailsService(myUserDetailsService) //指定查询用户的service对象
        .tokenRepository(persistentTokenRepository);  //指定token持久化对象

4.修改test接口,权限改回来

@PreAuthorize("hasAnyAuthority('p1')")
@GetMapping("/test")
public String test() {
    return "您可以访问/test接口";
}

5.登陆页面修改




    
    Title


    


记住我:

重启,访问:

第二次启动记得将建表的代码注释掉

现在,我们关闭浏览器。直接访问:localhost:8080/test,看会不会让我们再登录。

直接访问到了,不会跳转到登录页面

保存到数据库的token有效期默认为两周,

我们也可以自己设置

.tokenValiditySeconds(10086)  //设置token有效时间,单位秒

完整的配置类代码:

import javax.sql.DataSource;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDetailsService myUserDetailsService;
    @Autowired
    private DataSource dataSource;


    @Bean
    public PasswordEncoder encoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Bean
    //返回持久化token到数据库的bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        //自动创建所需要的表,第二次启动需要注释掉不然报错
        //jdbcTokenRepository.setCreateTableonStartup(true);
        return jdbcTokenRepository;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);
        http.formLogin()        //开启表单登录
                .loginPage("/login.html")   //定义登录页面
                .loginProcessingUrl("/mylogin")   //自定义登录请求,form表单中的action就是它
                .usernameParameter("myName")   //自定义用户名参数,默认表单中必须是username
                .passwordParameter("myPass")   //自定义密码参数,默认表单中必须是password
                .successForwardUrl("/success")
                .failureForwardUrl("/unsuccess");

        http.authorizeRequests()    //认证相关
                .antMatchers("/login.html").permitAll()  //放行登录页面
                .antMatchers("/unsuccess").permitAll()
                .antMatchers("/Main.html").denyAll()  //拒绝访问
                //访问hello请求需要有p1权限
                .antMatchers("/hello").hasAnyAuthority("p1","p2")
                //.antMatchers("/test").hasAnyRole("admin","test")
                .anyRequest().authenticated();   //其余请求都必须认证后才能访问

        http.rememberMe()  //开启记住我功能
                .tokenValiditySeconds(10086)  //设置token有效时间,单位秒
                .rememberMeParameter("remember")  //自定义记住我参数名,remember-me
                .userDetailsService(myUserDetailsService) //指定查询用户的service
                .tokenRepository(persistentTokenRepository());  //token持久化


        //从 Spring Security4 开始 CSRF 防护默认开启。默认会拦截请求。进行 CSRF 处理。
        // CSRF 为了保证不是其他第三方网站访问,
        // 要求访问时携带参数名为_csrf 值为 token(token 在服务端产生)的内容,如果token 和服务端的 token 匹配成功,则正常访问。
        //所以关闭csrf请求才能成功
        http.csrf().disable();
    }
}
8.退出登录

退出就很简单了,我们找个页面,写个a标签都可以,security默认的退出就是"/logout"




    
    Title


登录成功退出


如果你想自定义退出路径也可以:

即可。

jwt+oauth2待更新
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/695216.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号