前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring 全家桶之 Spring Security(一)

Spring 全家桶之 Spring Security(一)

作者头像
RiemannHypothesis
发布2022-08-19 15:25:20
5210
发布2022-08-19 15:25:20
举报
文章被收录于专栏:Elixir

Spring Security

Introduction

  Spring Security是基于Spring的安全框架,Spring Security提供全面的安全性解决方案,同时在Web Request和Method处理身份认证和授权,在Spring Framework基础上,Spring Security充分利用了Soring的 DI和AOP特性,为应用系统提供了声明式的安全访问控制功能,是一个轻量级的框架,可以很好的与Spring及Spring MVC集成

核心功能

  1. 认证(Who are you?)
  2. 授权(What you can do?)原理   基于Servlet Filter AOP实现认证和授权

Spring Security 最佳实践

使用系统自定义用户及yml中自定义的用户进行登录

  1. 创建Maven项目
  2. 加入依赖,SpringBoot web stater和security-stater
代码语言:javascript
复制
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-parent</artifactId>
    <version>2.1.5.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

3.创建启动SpringBoot启动类

代码语言:javascript
复制
@SpringBootApplication
public class SecurityApplication {

    public static void main(String[] args) {

        SpringApplication.run(SecurityApplication.class,args);
    }
}

4.创建controller包及Contrller控制器

代码语言:javascript
复制
@RestController
public class HelloSecurityController {

    @GetMapping("/security")
    public String helloSecurity(){
        return "Hello Spring Security!";
    }
}

5.启动SecurityApplication,控制台会生成密码,请看下图所示

image.png
image.png

浏览器地址栏输入http://localhost:8080/

image.png
image.png

输入用户名user及控制台生成的密码,即可登录系统访问HelloSecurityController

image.png
image.png

如果密码输入错误,则会有相应的提示

image.png
image.png

6.以上用户名密码都是由系统自动生成的,如果需要自定义用户名密码则需要在配置文件中进行配置,重新启动,输入设置的用户名密码即可登录

代码语言:javascript
复制
spring:
  security:
    user:
      name: admin
      password: admin

7.关闭登录验证对启动类进行修改,{}中可以放入多个配置类

代码语言:javascript
复制
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class SecurityApplication {

    public static void main(String[] args) {

        SpringApplication.run(SecurityApplication.class,args);
    }
}

使用设置在内存中的用户进行登录

&emsp;&emsp;继承WebSecurityConfigurerAdapter,重写configure方法来控制安全管理的内容,将重写的类交由Spring IOC进行管理,可以自定义认证功能,重写是需要使用两个注解@Configuration和@EnableWebSecurity

代码语言:javascript
复制
@Configuration//表示该类是一个配置类,返回值是Java对象,由SpringIOC管理,相当于配置xml文件
@EnableWebSecurity //表示启用SpringSecurity安全框架功能
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        PasswordEncoder pe = passwordEncoder();

        // 设置内存中用户名和密码
        auth.inMemoryAuthentication().withUser("IronMan").password(pe.encode("12345")).roles();
        auth.inMemoryAuthentication().withUser("SpiderMan").password(pe.encode("12345")).roles();
        auth.inMemoryAuthentication().withUser("Thor").password(pe.encode("thor")).roles();
    }

    // 密码加密类,该类由SpringIOC进行管理,id默认为passwordEncoder
    @Bean
    public PasswordEncoder passwordEncoder(){
        // 实现密码加密
        return new BCryptPasswordEncoder();
    }
}

启动前关闭启动类上的exclude中的内容,启动成功后使用设置的用户名密码进行登录系统,如果改配置类中设置的密码没有加密会报错“java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"”

基于ROLE的身份认证

修改config包中的MyWebSecurityConfig类,给用户设置角色,代码如下:

代码语言:javascript
复制
/**
 * prePostEnabled = true表示可以使用@PreAuthorize注解和@PostAuthorize方法级别的注解
 */
@Configuration//表示该类是一个配置类,返回值是Java对象,由SpringIOC管理,相当于配置xml文件
@EnableWebSecurity //表示启用SpringSecurity安全框架功能
@EnableGlobalMethodSecurity(prePostEnabled = true) //启用方法级别的安全控制
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        PasswordEncoder pe = passwordEncoder();

        // 设置内存中用户名和密码,并设置角色,一个用户可以有多个角色
        auth.inMemoryAuthentication().withUser("IronMan").password(pe.encode("12345")).roles("admin");
        auth.inMemoryAuthentication().withUser("SpiderMan").password(pe.encode("12345")).roles("user");
        auth.inMemoryAuthentication().withUser("Thor").password(pe.encode("thor")).roles("user","admin");
    }

    // 密码加密类,该类由SpringIOC进行管理,id默认为passwordEncoder
    @Bean
    public PasswordEncoder passwordEncoder(){
        // 实现密码加密
        return new BCryptPasswordEncoder();
    }
}

修改HelloSecurityController,定义角色访问路径

代码语言:javascript
复制
@RestController
public class HelloSecurityController {

    @GetMapping("/security")
    public String sayHello(){
        return "使用内存中的用户信息进行认证";
    }

    //指定user和admin都可以访问的方法
    @GetMapping("/hello")
    @PreAuthorize(value = "hasAnyRole('admin','user')")
    public String helloUserAndAdmin(){
        return "user和admin角色都可以访问";
    }

    @GetMapping("/admin")
    @PreAuthorize(value = "hasAnyRole('admin')")
    public String helloAdmin(){
        return "只有admin可以访问";
    }
}

启动应用,使用IronMan/12345访问/hello

image.png
image.png

访问/admin路径

image.png
image.png

重启tomcat,使用Thor/thor访问/hello

image.png
image.png

访问/admin

image.png
image.png

实现了不同的角色拥有不同路径的访问权限

基于JDBC的用户认证

首先修改pom.xml,增加MySQL依赖及JPA

代码语言:javascript
复制
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.18</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

创建实体类UserInfo,添加get/set方法

代码语言:javascript
复制
@Entity
public class UserInfo {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String username;
    private String password;
    private String role;
}

创建持久层UserInfoDao,继承JpaRepository,定义方法findByUsername(String username)

代码语言:javascript
复制
public interface UserInfoDao extends JpaRepository<UserInfo,Long>{

    //按照username查询数据库
    UserInfo findByUsername(String username);
}

创建service层UserInfoService接口,并创建UserInfoServiceImpl实现类实现该接口

代码语言:javascript
复制
public interface UserInfoService {

    UserInfo findUserInfo(String username);
}

不要忘记@Service注解

代码语言:javascript
复制
@Service
public class UserInfoServiceImpl implements UserInfoService {

    @Autowired
    private UserInfoDao userInfoDao;

    @Override
    public UserInfo findUserInfo(String username) {
        return userInfoDao.findByUsername(username);
    }
}

配置application.properties

代码语言:javascript
复制
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;useSSL=false&amp;serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.database=mysql
# 声明创建表时使用InnoDB引擎,默认使用myisam
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

此时启动应用会在数据库中创建user_info表,接着需要初始化user_info表中的数据 在init包下面创建JDBCInit类

代码语言:javascript
复制
@Component
public class JDBCInit {

    @Autowired
    private UserInfoDao userInfoDao;

    @PostConstruct
    public void init(){

        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        UserInfo user1 = new UserInfo();
        user1.setUsername("IronMan");
        user1.setPassword(passwordEncoder.encode("12345"));
        user1.setRole("admin");

        userInfoDao.save(user1);

        UserInfo user2 = new UserInfo();
        user2.setUsername("thor");
        user2.setPassword(passwordEncoder.encode("12345"));
        user2.setRole("user");

        userInfoDao.save(user2);

    }
}

接着创建MyUserDetailService,该类继承框架包中的UserDetailService,作用类似于存储用户角色信息

代码语言:javascript
复制
@Component("myUserDetailService")
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private UserInfoService userInfoService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInfo userInfo = null;
        User user = null;
        if (username != null){
            userInfo = userInfoService.findUserInfo(username);
            if (userInfo != null){
                List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();
                GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_"+userInfo.getRole());
                grantedAuthorityList.add(authority);
                // 创建User类,框架中的User类
                user = new User(userInfo.getUsername(),userInfo.getPassword(),grantedAuthorityList);
            }
        }
        return user;
    }
}

修改MyWebSecurityConfig,将保存用信息到内存中的代码全部删除

代码语言:javascript
复制
/**
 * prePostEnabled = true表示可以使用@PreAuthorize注解和@PostAuthorize方法级别的注解
 */
@Configuration//表示该类是一个配置类,返回值是Java对象,由SpringIOC管理,相当于配置xml文件
@EnableWebSecurity //表示启用SpringSecurity安全框架功能
@EnableGlobalMethodSecurity(prePostEnabled = true) //启用方法级别的安全控制
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailService myUserDetailService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailService).passwordEncoder(new BCryptPasswordEncoder());
    }
}

HelloSecurityController的代码不变,启动应用,使用IronMan/12345登录,可以访问/hello,/admin

image.png
image.png
image.png
image.png

使用thor/12345登录,访问/hello,/admin

image.png
image.png
image.png
image.png
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-12-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring Security
    • Introduction
      • 核心功能
      • Spring Security 最佳实践
        • 使用系统自定义用户及yml中自定义的用户进行登录
          • 使用设置在内存中的用户进行登录
            • 基于ROLE的身份认证
              • 基于JDBC的用户认证
              相关产品与服务
              云数据库 MySQL
              腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档