当前位置:   article > 正文

SpringBoot中的Security简单入门实现_spring-boot-starter-security

spring-boot-starter-security

1 目录

  1. 理解什么是权限

  2. 学习Spring Security框架的基本概练和用法

  3. 能够使用Spring Security写一个入门级的安全应用

2 怎么在SpringBoot应用Security

为了让我们的接口能够根据用户的权限进行一定限制,我们引入了Security,通过权限,我们能让其在登陆后一段时间,能自由访问权限内的接口,但是不能访问权限外的接口方法。

为此我们需要对之前的项目进行改造,具体内容在以下几个部分;

2.1 导入maven依赖

  1. <!--       springboot启动器-->
  2.        <dependency>
  3.            <groupId>org.springframework.boot</groupId>
  4.            <artifactId>spring-boot-starter</artifactId>
  5.        </dependency>
  6. <!--       lombok-->
  7.        <dependency>
  8.            <groupId>org.projectlombok</groupId>
  9.            <artifactId>lombok</artifactId>
  10.            <optional>true</optional>
  11.        </dependency>
  12. <!--       test测试-->
  13.        <dependency>
  14.            <groupId>org.springframework.boot</groupId>
  15.            <artifactId>spring-boot-starter-test</artifactId>
  16.            <scope>test</scope>
  17.        </dependency>
  18. <!--       Web-->
  19.        <dependency>
  20.            <groupId>org.springframework.boot</groupId>
  21.            <artifactId>spring-boot-starter-web</artifactId>
  22.        </dependency>
  23. <!--       mybatis和mysql驱动-->
  24.        <!--       mybatis-->
  25.        <dependency>
  26.            <groupId>org.mybatis.spring.boot</groupId>
  27.            <artifactId>mybatis-spring-boot-starter</artifactId>
  28.            <version>${mybatis.springboot.version}</version>
  29.        </dependency>
  30.        <!--       mysqsl-->
  31.        <dependency>
  32.            <groupId>mysql</groupId>
  33.            <artifactId>mysql-connector-java</artifactId>
  34.            <version>${mysql.version}</version>
  35.        </dependency>
  36. <!--       分页-->
  37.        <dependency>
  38.            <groupId>com.github.pagehelper</groupId>
  39.            <artifactId>pagehelper-spring-boot-starter</artifactId>
  40.            <version>1.4.3</version>
  41.        </dependency>
  42.        <dependency>
  43.            <groupId>com.github.pagehelper</groupId>
  44.            <artifactId>pagehelper</artifactId>
  45.            <version>5.3.1</version>
  46.        </dependency>
  47. <!--       导入JPA依赖 -->
  48.        <dependency>
  49.            <groupId>org.springframework.boot</groupId>
  50.            <artifactId>spring-boot-starter-data-jpa</artifactId>
  51.        </dependency>
  52.        <dependency>
  53.            <groupId>mysql</groupId>
  54.            <artifactId>mysql-connector-java</artifactId>
  55.        </dependency>
  56. <!--       Swagger-->
  57.        <dependency>
  58.            <groupId>io.springfox</groupId>
  59.            <artifactId>springfox-swagger2</artifactId>
  60.            <version>2.9.2</version>
  61.        </dependency>
  62.        <dependency>
  63.            <groupId>io.springfox</groupId>
  64.            <artifactId>springfox-swagger-ui</artifactId>
  65.            <version>2.9.2</version>
  66.        </dependency>
  67.        <dependency>
  68.            <groupId>net.minidev</groupId>
  69.            <artifactId>json-smart</artifactId>
  70.        </dependency>
  71. <!--       redis-->
  72.        <dependency>
  73.            <groupId>org.springframework.boot</groupId>
  74.            <artifactId>spring-boot-starter-data-redis</artifactId>
  75.        </dependency>
  76. <!--     日志收集-->
  77.        <dependency>
  78.            <groupId>org.aspectj</groupId>
  79.            <artifactId>aspectjweaver</artifactId>
  80.            <version>1.9.7</version>
  81.            <scope>compile</scope>
  82.        </dependency>
  83. <!--       Security-->
  84.        <dependency>
  85.            <groupId>org.springframework.boot</groupId>
  86.            <artifactId>spring-boot-starter-security</artifactId>
  87.        </dependency>

2.2 创建一个index门户接口

当前接口用于测试我们的权限认证和授权

  1. import lombok.extern.slf4j.Slf4j;
  2. import org.springframework.security.access.prepost.PreAuthorize;
  3. import org.springframework.security.core.Authentication;
  4. import org.springframework.security.core.context.SecurityContext;
  5. import org.springframework.security.core.context.SecurityContextHolder;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. @RestController
  9. @Slf4j
  10. public class IndexController {
  11.    //test 1 : anyone could access
  12.    @RequestMapping("/index")
  13.    public String getAllUsers() {
  14.        log.info("访问 index..");
  15.        return "访问 index....";
  16.   }
  17.    //test 2 : someone has the Authority:'cx:updates_user' could access
  18.    @RequestMapping("/users")
  19.    @PreAuthorize("hasAuthority('cx:updates_user')")// 授权:有cx:updates_user权限才能做该操作 否则报错403
  20.    public String update() {
  21.        //获取上下文
  22.        SecurityContext securityContext = SecurityContextHolder.getContext();
  23.        Authentication authentication = securityContext.getAuthentication();
  24. //在页面返回当前登录用户的所有权限
  25.        return authentication.toString();
  26.   }
  27. }
 

2.3 创建Security配置类WebSecurityConfigure

  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  5. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  6. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  7. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  8. import org.springframework.security.core.userdetails.UserDetailsService;
  9. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  10. import org.springframework.security.crypto.password.PasswordEncoder;
  11. // 这个有了以下任何一个注解都可以不用这个注解
  12. // @Configuration
  13. // 这个表示启用Web安全的注解,如果你已经是是一个web 项目,不需要使用此注解,
  14. // @EnableWebSecurity //Springboot的自动配置机制WebSecurityEnablerConfiguration已经引入了该注解
  15. //开启这个来判断用户对某个控制层的方法是否具有访问权限(见ProductController的@PreAuthorize)
  16. // 这个注解很重要,如果没有这个注解,那么Controller里的方法将不受约束,只要登录成功就能访问。
  17. @EnableGlobalMethodSecurity(prePostEnabled = true) //至关重要的注解,缺失导致验证不起效
  18. @EnableWebSecurity
  19. public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {
  20.    @Autowired
  21.    private UserDetailsService userDetailsService;
  22.    // 参数: HttpSecurity http
  23.    //**http.authorizeRequests()**
  24.    // 下添加了多个匹配器,每个匹配器用来控制不同的URL接受不同的用户访问。
  25.    // 简单讲,http.authorizeRequests()就是在进行请求的权限配置。
  26.    @Override
  27.    protected void configure(HttpSecurity http) throws Exception {
  28.        //第二步:我们用我们自己的数据库数据来完成权限验证
  29.        http.authorizeRequests()
  30.               .antMatchers("/index").permitAll()//放行
  31.               .anyRequest().authenticated()
  32.               .and()
  33.               .formLogin()
  34.   }
  35.    
  36.    //这里配置密码为 BCrypt 加密方式,这样创建用户时,会对密码进行加密。而不是明文存储。
  37.    @Bean
  38.    public PasswordEncoder passwordEncoder() {
  39.        return new BCryptPasswordEncoder();
  40.   }
  41. }

2.4 配置yml文件

spring中加入security(注意层级)

spring:
  security:
    user:
      name: jing
      password: 1234

完成到这里,我们已经能够对无权访问的接口进行限制了。

尝试访问接口

http://localhost:8080/users

接下来进一步完善我们的security

2.5 创建UserDetails

使用之前的User实体类 实现UserDetails接口并实现其方法

  1. package com.wanxi.springboot1018.entity;
  2. import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  3. import io.swagger.annotations.ApiModel;
  4. import io.swagger.annotations.ApiModelProperty;
  5. import lombok.Data;
  6. import org.springframework.security.core.GrantedAuthority;
  7. import org.springframework.security.core.userdetails.UserDetails;
  8. import org.springframework.stereotype.Component;
  9. import java.io.Serializable;
  10. import java.util.Collection;
  11. import java.util.HashSet;
  12. import java.util.Set;
  13. @Data
  14. @Component
  15. @ApiModel(value = "用户",description = "用于描述用户对象")
  16. @JsonIgnoreProperties({"enabled","accountNonExpired", "accountNonLocked", "credentialsNonExpired", "authorities"}) //避免把userdetail接口的方法序列化
  17. public class User extends Base implements UserDetails {
  18. @ApiModelProperty(value = "用户ID",example = "123")
  19. private int id=0;
  20. @ApiModelProperty(value = "用户密码",example = "abc")
  21. private String password="";
  22. @ApiModelProperty(value = "用户姓名",example = "jing")
  23. private String username="";
  24. @ApiModelProperty(value = "用户电话",example = "180****8963")
  25. private String tel="";
  26. @ApiModelProperty(value = "生产日期",example = "2000-08-13")
  27. private String birthday="";
  28. @ApiModelProperty(value = "性别",example = "男")
  29. private String sex="";
  30. @ApiModelProperty(value = "授权变量",example = "")
  31. private Set<? extends GrantedAuthority> authorities= new HashSet<>();
  32. private Boolean A ;
  33. private Boolean B ;
  34. public Boolean getA() {
  35. return A;
  36. }
  37. public void setA(Boolean a) {
  38. A = a;
  39. }
  40. public Boolean getB() {
  41. return B;
  42. }
  43. public void setB(Boolean b) {
  44. B = b;
  45. }
  46. @Override
  47. public Collection<? extends GrantedAuthority> getAuthorities() {
  48. return authorities;
  49. }
  50. //账号是否过期 count has expired
  51. @Override
  52. public boolean isAccountNonExpired() {
  53. return true;
  54. }
  55. //账号是否上锁 count has locked
  56. @Override
  57. public boolean isAccountNonLocked() {
  58. return true;
  59. }
  60. //令牌是否过期 报错:credentials have expired
  61. @Override
  62. public boolean isCredentialsNonExpired() {
  63. return true;
  64. }
  65. //是否启用 报错:User is disabled 由于没有status状态这个字段,默认启用。
  66. @Override
  67. public boolean isEnabled() {
  68. return true;
  69. }
  70. @Override
  71. public String toString() {
  72. return "User{" +
  73. "id=" + id +
  74. ", password='" + password + '\'' +
  75. ", name='" + username + '\'' +
  76. '}';
  77. }
  78. @Override
  79. public String getPassword() {
  80. return this.password;
  81. }
  82. @Override
  83. public String getUsername() {
  84. return this.username;
  85. }
  86. }

2.6 创建GrantedAuthority

新建一个类:Permission 表示用户的一个权限

并实现GrantedAuthority接口

(注意,这个实体类的成员一定要和数据库的字段相对应)

  1. import lombok.Data;
  2. import org.springframework.security.core.GrantedAuthority;
  3. import java.util.Date;
  4. /**
  5. *@description: 授予的权限信息,要实现GrantedAuthority
  6. */
  7. @Data
  8. // 不使用@Builder时以下@AllArgsConstructor和@NoArgsConstructor都可以不要,使用了就要需要,不然mybatis构建对象时会出错。
  9. //@Builder
  10. //@AllArgsConstructor
  11. //@NoArgsConstructor
  12. public class Permission implements GrantedAuthority {
  13.    private Integer id;
  14.    private Integer pid;
  15.    private String name;
  16.    private String value;
  17.    private String icon;
  18.    private Integer type;
  19.    private String uri;
  20.    private Integer status;
  21.    private Date createTime;
  22.    private String sort;
  23.    //获取权限
  24.    @Override
  25.    public String getAuthority() {
  26.        // 这里返回的内容要和Controller里的@PreAuthorize("hasAuthority('wx:product:read')")匹配
  27.        return this.value;
  28.   }
  29. }

2.7 创建UserDetailsService

不同与之前的UserService,这个接口实现类会调用userService的方法,并被Security调用

  1. import com.wanxi.springboot1018.entity.Permission;
  2. import com.wanxi.springboot1018.entity.User;
  3. import com.wanxi.springboot1018.service.UserService;
  4. import org.springframework.security.core.userdetails.UserDetails;
  5. import org.springframework.security.core.userdetails.UserDetailsService;
  6. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  7. import org.springframework.stereotype.Service;
  8. import javax.annotation.Resource;
  9. import java.util.HashSet;
  10. import java.util.List;
  11. /**
  12. *@description: UserDetailsService的实现类,Security 安全框架会调用这个接口的loadUserByUsername。
  13. *               这个类是Security 框架定义的接口,不是我们自己业务定义的接口,
  14. *               要想Security 按照我们的逻辑起作用,我们需要实现它
  15. */
  16. @Service("userDetailsService")
  17. public class UserDetailServiceImpl implements UserDetailsService {
  18.    @Resource
  19.    UserService userService;
  20.    @Override
  21.    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  22.        //通过用户名 访问数据库拿到 当前用户对象
  23.        User user= userService.getUserByName(username);
  24.        //一定要设置为加密后的密码
  25.        //user.setName(username);
  26.        user.setName("jing");
  27.        user.setPassword("$2a$10$1M8F40YGBvgZrp0/UYtGxOTTFjiWxdXik1x1b.qliRk2tnOyWBv2i");
  28.        // 紧接着 调用getPermissionsByUserId 方法 获取当前用户的 权限(用户->角色->权限)
  29.        List<Permission> permissionList= userService.getPermissionsByUserId(user.getId());
  30.        // 创建 HashSet 取代 List
  31.        HashSet<Permission> permissions = new HashSet<>(permissionList);
  32.        // 存入user对象
  33.        user.setAuthorities(permissions);
  34.        // 返回对象,包含该用户的所有权限
  35.        return user;
  36.   }
  37. }

2.8 修改Config配置类

  1. http.authorizeRequests()
  2.               .antMatchers("/index").permitAll()//放行
  3.               .anyRequest().authenticated()
  4.               .and()
  5.               .formLogin()
  6.               .and()
  7.                // 这一步,告诉Security 框架,我们要用自己的UserDetailsService实现类
  8.                // 来传递UserDetails对象给框架,框架会把这些信息生成Authorization对象使用
  9.               .userDetailsService(userDetailsService);

在这里加上后面的userDetailsService。

@PreAuthorize("hasAuthority('cx:updates_user')")// 授权:有cx:updates_user权限才能做该操作 否则报错403

这里的注解已经加上了

所以直接访问测试即可

2.10 访问测试

登录

报错403:权限不够

证明了我们的权限能够正常生效。并且拦截正确

2.11 其他验证:

一、把@PreAuthorize("hasAuthority('wx:product:read')") 这个值改一改,改成没有的试试

如果有注解但是没有权限:

如果没有注解:能够拿到数据

二、 把Config 类的@EnableGlobalMethodSecurity注解去掉,看看权限是否生效

恢复上面的注解

我们吧@EnableGlobalMethodSecurity去掉后,

尝试直接跨过登录访问无权限限制的接口:

尝试直接跨过登录访问无权限限制的接口:回到了登录界面

登录后再进行无权访问的接口:

可以看到,只要能登陆进来,就能访问,权限就是摆设

3

3.1

4 概念图

这些图需要在看完代码构成后,才能真正去的立体化去理解流程。

4.1 权限六表概念图

4.2 Secutity 流程图

4.3 Secutity 构成

以上是10月31日对29日的Security学习进行日常总结。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/701983
推荐阅读
相关标签
  

闽ICP备14008679号