02 Web 权限方案

Web 权限方案

用户认证

设置登录系统的账号、密码
方式一:在 application.properties

spring.security.user.name=uzi
spring.security.user.password=123

方式二:配置类实现接口

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //对密码进行加密
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode("123");
        auth.inMemoryAuthentication().withUser("uzi").password(encode).roles("admin");
    }

    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }
}

方式三:编写类实现接口

@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService UserDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(UserDetailsService).passwordEncoder(password());

    }

    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }

}

@Service("UserDetailsService")
public class MyUserDetails implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        return new User("uzi",new BCryptPasswordEncoder().encode("123"),auths);
    }
}

实现数据库认证来完成用户登录

  1. 引入 MyBatis-Plus MySQL 依赖
  2. User 的实体类
  3. 整合 MybatisPlus 制作 mapper
@Service
public class MyUserDetails implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名 查询数据库
        QueryWrapper<Users> QueryWrapper = new QueryWrapper<Users>();
        QueryWrapper.eq("username",username);
        Users users = userMapper.selectOne(QueryWrapper);
        //判断
        if (users == null) {
            throw new UsernameNotFoundException("用户名不存在!");
        }

        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        //从 users 对象里面 得到用户名和密码
        return new User(users.getUsername(),
                new BCryptPasswordEncoder().encode(users.getPassword()),auths);
    }
}

@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());

    }

    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }

}

自定义用户登录页面

编写配置类放行登录页面以及静态资源

@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());

    }

    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //自定义自己编写的登录页面
        http.formLogin()
                .loginPage("/login.html")// 登录页面
                .loginProcessingUrl("/user/login") //登录访问路径 登录之后交给哪个 controller  由SpringSecurity完成
                .defaultSuccessUrl("/test/index").permitAll() //登陆成功之后 跳转路径
                .and().authorizeRequests()
                    .antMatchers("/","/test/hello","user/login").permitAll() //设置那些路径能够直接访问,不需要认证
                .anyRequest().authenticated()
                .and().csrf().disable();//关闭 csrf 防护
    }
}

controller 控制器

@RestController
@RequestMapping("test")
public class TestController {

    @GetMapping("hello")
    public String hello(){
        return "hello security";
    }

    @GetMapping("index")
    public String index(){
        return "hello index";
    }
}

login.html

<form action="/login"method="post">
用户名:<input type="text"name="username"/><br/>
密码:<input type="password"name="password"/><br/>
<input type="submit"value="提交"/>
</form>

注意:页面提交方式必须为 post 请求,所以上面的页面不能使用,用户名,密码必须为username,password

用户授权

1.1 基于用户或权限进行访问控制

1.1.1 hasAuthority() 方法

如果当前的主体具有指定的权限,则返回true,否则返回false(一个权限)

  1. 在配置类设置当前访问地址有哪些权限
//表示当前登录用户 只有admins 权限 才能访问这个路径                .antMatchers("/test/index").hasAuthority("admins")
  1. 在 UserDetailsService ,把返回 users 对象设置权限

List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admins");

测试: 如果返回403 就是没有权限

1.1.2 hasAnyAuthority() 方法

如果当前的主体具有其中的某一个权限,则返回true,否则返回false

.antMatchers("/test/index").hasAnyAuthority("admins,manager")

1.1.3 hasRole() 方法

如果用户具备给定角色就允许访问,否则出现 403。
如果当前主体具有指定的角色,则返回 true。

// 3. hasRole() 方法   源码变成 ROLE_sale
.antMatchers("/test/index").hasRole("sale")

在 UserDetailsService ,把返回 users 对象设置权限

List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_sale");

1.1.4 hasAnyRole() 方法

表示用户具有任何一个条件都可以访问

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

@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());

    }

    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
     //配置没有权限访问跳转自定义页面
     http.exceptionHandling().accessDeniedPage("/unauth.html");
        //自定义自己编写的登录页面
        http.formLogin()
                .loginPage("/login.html")// 登录页面
                .loginProcessingUrl("/user/login") //登录访问路径 登录之后交给哪个 controller  由SpringSecurity完成
                .defaultSuccessUrl("/test/index").permitAll() //登陆成功之后 跳转路径
                .and().authorizeRequests()
                    .antMatchers("/","/test/hello","user/login").permitAll() //设置那些路径能够直接访问,不需要认证
                    //表示当前登录用户 只有admins 权限 才能访问这个路径
                    //1.hasAuthority() 方法
                    //.antMatchers("/test/index").hasAuthority("admins")
                    //2.hasAnyAuthority() 方法
                    //.antMatchers("/test/index").hasAnyAuthority("admins,manager")
                    // 3. hasRole() 方法   源码变成 ROLE_sale
                    .antMatchers("/test/index").hasRole("sale")
                .anyRequest().authenticated()
                .and().csrf().disable();//关闭 csrf 防护
    }
}

认证授权(注解使用)

@Secured

用户具有某个角色 可以访问方法

  1. 在 启动类(配置类) 开启注解

@EnableGlobalMethodSecurity(securedEnabled = true)

  1. 在 controller的方法上面使用注解,开启角色
  @GetMapping("update")
  @Secured({"ROLE_sale","ROLE_manager"})
  public String update(){
     return "hello update";
 }
  1. 在 UserDetailsService ,把返回 users 对象设置角色

List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_sale");

@PreAuthorize

进入方法前做的权限认证

  1. 在 启动类(配置类) 开启注解

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

  1. 在 controller的方法上面使用注解
    @GetMapping("update")
    //@Secured({"ROLE_sale","ROLE_manager"})
    @PreAuthorize("hasAnyAuthority('admins')")//hasAuthority 等等
    public String update(){
        return "hello update";
    }

@PostAuthorize

进入方法之后做的权限认证

  1. 在 启动类(配置类) 开启注解

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

@PreFilter

进入控制器之前对数据进行过滤

@RequestMapping("getTestPreFilter")
@PreAuthorize("hasRole('ROLE_管理员')")
@PreFilter(value = "filterObject.id%2==0")
@ResponseBody
public List<UserInfo> getTestPreFilter(@RequestBody List<UserInfo> 
list){
     list.forEach(t-> {
     	System.out.println(t.getId()+"\t"+t.getUsername());
     });
    return list;
}

@PostFilter

进入控制器之后对数据进行过滤

用户注销

  1. 在配置类添加退出的配置类
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //退出
  http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll();

自动登录

1637824802555

@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private DataSource dataSource;

    //注入数据源,配置操作数据库对象
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());

    }

    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //退出
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll();
        //配置没有权限访问跳转自定义页面
        http.exceptionHandling().accessDeniedPage("/unauth.html");
        //自定义自己编写的登录页面
        http.formLogin()
                .loginPage("/login.html")// 登录页面
                .loginProcessingUrl("/user/login") //登录访问路径 登录之后交给哪个 controller  由SpringSecurity完成
                .defaultSuccessUrl("/success.html").permitAll() //登陆成功之后 跳转路径
                .and().authorizeRequests()
                    .antMatchers("/","/test/hello","user/login").permitAll() //设置那些路径能够直接访问,不需要认证
                    //表示当前登录用户 只有admins 权限 才能访问这个路径
                    //1.hasAuthority() 方法
//                    .antMatchers("/test/index").hasAuthority("admins")
                    //2.hasAnyAuthority() 方法
                    //.antMatchers("/test/index").hasAnyAuthority("admins,manager")
                    // 3. hasRole() 方法   源码变成 ROLE_sale
                    .antMatchers("/test/index").hasRole("sale")
                .anyRequest().authenticated()
                .and().rememberMe().tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(60)//设置有效时常
                .userDetailsService(userDetailsService)
                .and().csrf().disable();//关闭 csrf 防护
    }
}


CSRF 理解

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click
attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

从 Spring Security 4.0 开始,默认情况下会启用 CSRF 保护,以防止 CSRF 攻击应用程序,Spring Security CSRF 会针对 PATCH,POST,PUT 和 DELETE 方法进行防护。

在登录页面添加一个隐藏域:

<input 
type="hidden"th:if="${_csrf}!=null"th:value="${_csrf.token}"name="_csrf
"/

posted @   flypiggg  阅读(128)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示