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

记录自己学习springSecurity基本使用

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

记录自己学习springSecurity基本使用

1.什么是SpringSecurity,能做什么?

它是一个安全框架,主要能帮助我们做认证(你是谁)和授权(你能做什么)两大功能。

2.Springboot集成SpringSecurity(体验版)

1.首先导入依赖

    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.6.RELEASE
    
    
        1.8
    

    
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            org.springframework.boot
            spring-boot-starter-security
        

        
            org.projectlombok
            lombok
            1.18.20
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

 2.编写一个控制器。写一个映射方法。前端访问这个URL。你会发现会被Security拦截,需要输入用户名和密码。用户名security默认是user。密码默认是程序控制台上打印的。(Using generated security password: xxxxxxxx)

@Controller
public class HelloSecurityController {
    
    @GetMapping("/helloSecurity")
    @ResponseBody
    public String helloSecurity(){
        return "helloSecurity";
    }
}

3. 如果想要打破默认的security的用户名和密码(自定义登录与密码),只需在yaml配置如下:

spring:
  security:
    user:
      name: admin
      password: admin
3.基于内存版本的登录验证与角色权限管理

假设现在有一个需求是这样的:给你一个部门管理系统。

系统有两个角色:USER,ADMIN。

有两个功能:

        1)查看部门用户列表 (USER,ADMIN角色都可以使用)

        2)删除部门某用户(ADMIN角色才能使用)

1. 编写一个类继承WebSecurityConfigurerAdapter。重写configura方法。在方法中加入用户名和密码。并且在配置类上开启Security功能(@EnableWebSecurity)和

@EnableGlobalMethodSecurity(prePostEnabled = true)

注意:security5版本以上,需要密码加密。不然会出现一个passwordEncoder null 的异常.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    PasswordEncoder passwordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder.encode("123456")).roles("ADMIN");
        auth.inMemoryAuthentication().withUser("zs").password(passwordEncoder.encode("123456")).roles("USER");
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

其次,在controller中加入这两个功能代码如下:

    @GetMapping("/show/userList")
    @PreAuthorize(value = "hasAnyRole('USER','ADMIN')")
    @ResponseBody
    public String showUserList(){
        return "展示部门用户列表(角色要求:USER/ADMIN)";
    }

    
    @GetMapping("/deleteUser")
    @PreAuthorize(value = "hasAnyRole('ADMIN')")
    @ResponseBody
    public String deleteUser(){
        return "删除某用户(角色要求:ADMIN)";
    }

2. 其次在用浏览器测试,你会发现zs,admin 都可以使用展示部门列表,但是删除功能只能是admin用户才可以。

注意:浏览器在测试的时候没换一个账户时,需要清除浏览器缓存在测试。 如果出现403错误,就表示被拒绝了,因为403错误页面是Springboot自带的。

3.最佳实战:基于数据库版本

前面展示了基于内存版本的,现在升级为基于数据库版本的.

现在想要实现的是:项目首先进入登录页面,登录部门管理系统。登录成功后,部门管理系统会根据不同的角色控制他们的权限功能。

补充:SpringSecurity框架中的UserDetailsService 和 User 类
UserDetails: 是一个接口,用来获取用户权限、密码、账户、判断账户是否锁定等操作。 

user:是一个SpringSecurity高度抽象的用户类,实现了UserDetails接口。

所以如果你想基于数据库版本的。就必须实现UserDetailsService接口,重写里面的

public UserDetails loadUserByUsername(String username); 根据username去数据库登录登录校验查询。然后构建一个UserDetails 返回给security框架。

准备工作:

 准备sys_user表,sys_role表,sys_user_role_relation表。

sys_user:

+-----------------------+--------------+------+-----+---------+----------------+
| Field                 | Type         | Null | Key | Default | Extra          |
+-----------------------+--------------+------+-----+---------+----------------+
| id                    | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| realName              | varchar(255) | YES  |     | NULL    |                |
| username              | varchar(255) | YES  |     | NULL    |                |
| password              | varchar(255) | YES  |     | NULL    |                |
| accountNonExpired     | tinyint(1)   | YES  |     | 1       |                |
| accountNonLocked      | tinyint(1)   | YES  |     | 1       |                |
| credentialsNonExpired | tinyint(1)   | YES  |     | 1       |                |
| enabled               | tinyint(1)   | YES  |     | 1       |                |
| createTime            | date         | YES  |     | NULL    |                |
+-----------------------+--------------+------+-----+---------+----------------+

sys_role:

+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| role     | varchar(255) | YES  |     | NULL    |                |
| describe | varchar(255) | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

sys_user_role_relation:

+--------+------------+------+-----+---------+-------+
| Field  | Type       | Null | Key | Default | Extra |
+--------+------------+------+-----+---------+-------+
| userId | bigint(20) | NO   |     | NULL    |       |
| roleId | bigint(20) | NO   |     | NULL    |       |
+--------+------------+------+-----+---------+-------+

Dao层框架自定义,本次使用mybatisPlus。用mybatisplus自动生成对应的dao层代码。所以dao层代码全部省略了。

加入themeleaf依赖:


            org.springframework.boot
            spring-boot-starter-thymeleaf
        
  前端页面(随便写一下):

登录页面:




    Spring Security Example 


    
    
        Invalid username and password.
    
    
        You have been logged out.
    
    

系统主页:




    
    主页


    查看用户
删除用户

查看用户列表页面:




    
    展示用户


展示所有用户(角色要求:ADMIN/USER)

删除用户页面:




    
    Title


删除用户(角色要求:ADMIN)

后端Java:

    自定义一个实体类MySecurityUser implement UserDetails。重写里面所有的方法。

    编写一个UserDetailsService的实现类,重写loadUserByUsername方法。在方法中查询数据库构建MySecurityUser对象返回即可。

    在WebSecurityConfigurerAdapter 这个类中改版不要基于内存的了,而是基于UserDetailsService的。

1.
@Data
public class MySecurityUser implements UserDetails {
    private Long id;
    private String username;
    private String realName;
    private String password;
    private Integer accountNonExpired;
    private Integer accountNonLocked;
    private Integer credentialsNonExpired;
    private Integer enabled;
    private Date createTime;
    List authorityList;     // 该用户的角色列表集合

    @Override
    public Collection getAuthorities() {
        return authorityList;
    }

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

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

    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired==1;
    }

    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked==1;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired==1;
    }

    @Override
    public boolean isEnabled() {
        return enabled==1;
    }
}

2.

@Component("MyUserDetailsImpl")
public class MyUserDetailsImpl implements UserDetailsService {
    @Autowired
    ISys_userService iSys_userService;
    @Autowired
    ISys_user_role_relationService user_role_relationService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        MySecurityUser resUser = null;
        // 根据username查询
        Sys_user sys_user = iSys_userService.getOne(new QueryWrapper().eq("username", username));
        if(sys_user!=null){
            // 构建一个UserDetails对象给框架。
            resUser = new MySecurityUser();
            BeanUtils.copyProperties(sys_user,resUser);
            // 查询该username用户的权限
            List authorities = user_role_relationService.getRoleByUserId(resUser.getId());
            resUser.setAuthorityList(authorities);
        }
        return resUser;
    }
}

// 我这里构建username用户权限的代码是如下。重点想表达的意思是:角色名字前面需要加入 ROLE_ 这是security框架规定的。
    @Override
    public List getRoleByUserId(Long userId) {
        List list = user_role_relationMapper.getRoleByUserId(userId);
        List collect = list.stream().map((role) -> {
            GrantedAuthority g = new SimpleGrantedAuthority("ROLE_" + role.getRole());
            return g;
        }).collect(Collectors.toList());
        return collect;
    }

3.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    @Autowired
    @Qualifier("MyUserDetailsImpl")
    UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 改版基于userDetailsService 来实现数据库版本。
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // 表示哪些请求需要被放行。(首页/登录页/静态资源等允许放行)
                .antMatchers("/", "/static/**").permitAll()
                //而其他的请求都需要认证
                .anyRequest()
                .authenticated()
                .and()
                // 设置自定义的登录页面url
                .formLogin()
                .loginPage("/index.html")
                //指定处理登录请求的路径,对应form表单的action地址
                .loginProcessingUrl("/login.do").permitAll()
                // 指定错误时跳转到的url
                //.failureUrl("/error")
                // 指定登录成功跳转的url路径
                .defaultSuccessUrl("/main.html",true);
    }
}

4. 假设现在数据库中的数据是这样的:

张三(zs),拥有USER角色。

管理员(admin),拥有ADMIN,USER两个角色。

项目测试会发现张三只能使用展示用户列表功能, 删除功能只能是拥有ADMIN角色的管理员才可以使用。

总结:

本次记录自己学习security框架的成果。

版本3其实就是对于基于内存的改本。 加入了dao层的逻辑。

主要就是在

MyUserDetailsImpl类中 自己根据username 查询数据库自己构建一个UserDetails类扔给security框架而已。

如果我自己自己实现这种权限控制,大致思路就是在访问controller的时候,先经过过滤器或者拦截器。然后在此处判断该用户是否有资格访问该映射方法。如果有资格,放行。否者拦截。

后续有时间可以看看security的原理

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

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

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