精通 Spring Boot 系列 14

简介: 精通 Spring Boot 系列 14

阅读全文,约 18 分钟

这是江帅帅的第021篇原创

Spring Boot 的安全管理

1、Spring Security 是啥?

Spring Security 是 Spring 的一个安全模块,它很强大,但使用特别复杂。在安全管理这个领域,之前还有一个 Shiro 是比较受欢迎的,对于大部分的应用,Shiro 用得也比较成熟。Spring Boot 现在为 Spring Security 提供了自动化配置方案,用起来非常方便,所以大家慢慢就选择使用了 Spring Security 了。

最近,很多安全管理技术栈的组合长这样的:Spring Boot/Spring Cloud + Spring Security。

安全框架有两大主要操作:认证(Authentication)和授权(Authorization)。

2、Spring Security 简单使用

如何配置 Spring Security?非常简单,我们直接在类上继承 WebSecurityConfigurerAdapter 适配器即可,然后再用 @EnableWebSecurity 注解,再重写

configure() 方法来配置对应的安全信息。

我们还需要了解两个事情:用户认证、用户授权

2.1 用户认证

主要通过在 configureGlobal(AuthenticationManagerBuilder amb) 方法完成用户认证,然后通过 AuthenticationManagerBuilder 的 inMemoryAuthentication() 方法来添加用户,和用户的权限。

2.2 用户授权

主要通过 configure(HttpSecurity hs) 方法,完成用户授权,然后 HttpSecurity 的 authorizeRequests() 方法能设置多个 macher 节点来声明执行顺序,这样用户就能够访问多个 URL 模式了。

当你匹配了对应的请求路径之后,然后再执行安全处理。

Spring Security 的安全处理方法:

  • anyRequest:匹配所有路径
  • access:可以访问,当 Spring EL 的结果为 ture
  • anonymous:匿名可访问
  • denyAll:用户不能访问
  • fullyAuthenticated:用户完全认证可访问
  • hasAnyAuthority:参数代表权限,列出来任何一个的可访问
  • hasAnyRole:参数代表角色,列出来任何一个的可访问
  • hasAuthority:参数代表权限,列出来的可访问
  • hasIpAddress:参数代表 IP 地址,匹配的可访问
  • hasRole:参数角色,列出来的可访问
  • permitAll:用户可以任意访问
  • rememberMe:允许通过 remember-me 登录的用户访问
  • authenticated:用户登录后可访问

3、Spring Security 简单案例

1)编辑 pom.xml 文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.nx</groupId>
    <artifactId>springbootdata</artifactId>
    <version>1.0-SNAPSHOT</version>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/>
    </parent>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
 
  <dependencies>
 
      <!-- 添加spring-boot-starter-web模块依赖 -->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
 
      <!-- 添加spring-boot-starter-thymeleaf模块依赖 -->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
 
    <!-- 添加spring-boot-starter-security 依赖 -->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
 
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>
2)创建 NXPasswordEncoder 认证逻辑类
public class NXPasswordEncoder implements PasswordEncoder{
 
    @Override
    public String encode(CharSequence arg0) {
        return arg0.toString();
    }
 
    @Override
    public boolean matches(CharSequence arg0, String arg1) {
        return arg1.equals(arg0.toString());
    }
}
3)创建 AppSecurityConfigurer 密码器

目前,Spring Security 的密码存储格式为:{id}encodedPassword,其中 id 是用来找到对应的 PasswordEncoder,encodedPassword 用来指原始密码经过加密之后的密码。当我们想自定义密码器,必须实现 PasswordEncoder 接口。

@Configuration
public class AppSecurityConfigurer extends WebSecurityConfigurerAdapter{
 
    // 注入认证处理类,处理不同用户跳转到不同的页面
    @Autowired
    AppAuthenticationSuccessHandler appAuthenticationSuccessHandler;
 
    // 用户授权操作 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
 
        http.authorizeRequests()
        // 需要过滤静态资源
        .antMatchers("/login","/css/**","/js/**","/img/*").permitAll() 
        .antMatchers("/", "/home").hasRole("USER")
        .antMatchers("/admin/**").hasAnyRole("ADMIN", "DBA")
        .anyRequest().authenticated()
        .and()
        .formLogin().loginPage("/login").successHandler(appAuthenticationSuccessHandler)
        .usernameParameter("loginName").passwordParameter("password")
        .and()
        .logout().permitAll()
        .and()
        .exceptionHandling().accessDeniedPage("/accessDenied");
    }
 
    // 用户认证操作
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
 
        // 需要密码编码器
        auth.inMemoryAuthentication().passwordEncoder(new NXPasswordEncoder()).withUser("nx").password("888888").roles("USER");
       auth.inMemoryAuthentication().passwordEncoder(new NXPasswordEncoder()).withUser("admin").password("admin").roles("ADMIN","DBA");
    }
}
4)创建 AppAuthenticationSuccessHandler 认证成功处理类
@Component
public class AppAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
 
    // 通过 RedirectStrategy 对象负责所有重定向事务
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
 
    // 重写 handle 方法,通过 RedirectStrategy 对象重定向到指定的 url
    @Override
    protected void handle(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication)
            throws IOException {
        // 通过 determineTargetUrl 方法返回需要跳转的 url 
        String targetUrl = determineTargetUrl(authentication);
        redirectStrategy.sendRedirect(request, response, targetUrl);
    }
 
    // 从 Authentication 对象中提取角色提取当前登录用户的角色,并根据其角色返回适当的 URL。
    protected String determineTargetUrl(Authentication authentication) {
        String url = "";
 
        // 获取当前登录用户的角色权限集合
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
 
        List<String> roles = new ArrayList<String>();
 
        for (GrantedAuthority a : authorities) {
            roles.add(a.getAuthority());
        }
 
        // 判断不同角色跳转到不同的url
        if (isAdmin(roles)) {
            url = "/admin";
        } else if (isUser(roles)) {
            url = "/home";
        } else {
            url = "/accessDenied";
        }
        System.out.println("url = " + url);
        return url;
    }
 
    private boolean isUser(List<String> roles) {
        if (roles.contains("ROLE_USER")) {
            return true;
        }
        return false;
    }
 
    private boolean isAdmin(List<String> roles) {
        if (roles.contains("ROLE_ADMIN")) {
            return true;
        }
        return false;
    }
 
    public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
        this.redirectStrategy = redirectStrategy;
    }
 
    protected RedirectStrategy getRedirectStrategy() {
        return redirectStrategy;
    }
}
5)创建 NXController 控制器
@Controller
public class NXController {
 
    @RequestMapping("/")
    public String index() {
        return "index";
    }
 
     @RequestMapping(value = "/login")
    public String login() {
        return "login";
    }
 
    @RequestMapping("/home")
    public String homePage(Model model) {
        model.addAttribute("user", getUsername());
        model.addAttribute("role", getAuthority());
        return "home";
    }
 
    @RequestMapping(value = "/admin")
    public String adminPage(Model model) {
        model.addAttribute("user", getUsername());
        model.addAttribute("role", getAuthority());
        return "admin";
    }
 
    @RequestMapping(value = "/dba")
    public String dbaPage(Model model) {
        model.addAttribute("user", getUsername());
        model.addAttribute("role", getAuthority());
        return "dba";
    }
 
    @RequestMapping(value = "/accessDenied")
    public String accessDeniedPage(Model model) {
        model.addAttribute("user", getUsername());
        model.addAttribute("role", getAuthority());
        return "accessDenied";
    }
 
 
   @RequestMapping(value="/logout")
    public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
       // Authentication是一个接口,表示用户认证信息
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        // 如果用户认知信息不为空,注销
        if (auth != null){    
            new SecurityContextLogoutHandler().logout(request, response, auth);
        }
        // 重定向到login页面
        return "redirect:/login?logout";
    }
 
    private String getUsername(){
        // 从SecurityContex中获得Authentication对象代表当前用户的信息
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        System.out.println("username = " + username);
        return username;
    }
 
    private String getAuthority(){
        // 获得Authentication对象,表示用户认证信息。
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        List<String> roles = new ArrayList<String>();
 
        for (GrantedAuthority a : authentication.getAuthorities()) {
            roles.add(a.getAuthority());
        }
        System.out.println("role = " + roles);
        return roles.toString();
    }
}

最后,大家可以找一套前端页面,测试一下即可,非常简单的。

目录
相关文章
|
12天前
|
Java Spring
精通 Spring Boot 系列 08
精通 Spring Boot 系列 08
22 0
|
12天前
|
存储 Java Maven
精通 Spring Boot 系列 01
精通 Spring Boot 系列 01
7 0
|
12天前
|
Java 数据库 Spring
精通 Spring Boot 系列 12
精通 Spring Boot 系列 12
24 0
|
12天前
|
Java Spring
精通 Spring Boot 系列 11
精通 Spring Boot 系列 11
23 0
|
12天前
|
Java 数据库连接 数据库
精通 Spring Boot 系列 13
精通 Spring Boot 系列 13
23 0
|
12天前
|
存储 安全 Java
精通 Spring Boot 系列 15
精通 Spring Boot 系列 15
24 0
|
9月前
|
XML Java 数据库连接
为什么越来越多的人选择Spring Boot?
我们都知道,Spring是一个非常经典的应用框架,与其说是Java开发不如说是Spring开发,为什么现在越来越多的人会选择用Spring Boot呢?。要回答这个问题,还需要从Java Web开发的发展历史开始说起。
65 0
|
8月前
|
存储 缓存 算法
Spring Boot 中的 ConcurrentMapCacheManager
Spring Boot 中的 ConcurrentMapCacheManager
|
11月前
|
存储 JSON 安全
Spring Boot 安全
1.概述 在后端来说,安全主要就是控制用户访问,让对应权限的用户能访问到对应的资源,主要是两点: 认证 授权 认证,确定是谁。 授权,核实权限。 每个安全框架其实都是为了实现这两点。 目前常用的实现方式有如下几种: token JWT oauth spring security 前三种是理念,最后一种是开箱即食的框架。 2.token 2.1.理论 token ,也叫“令牌”,是验证用户身份的凭证。token的组成具有随意性,能标识用户身份即可。
99 0
|
XML 前端开发 IDE
5 分钟快速理解 Spring Boot
前言 Spring 是 Java 开发人员接触最多的框架,包括我在内的很多小伙伴只是对 Spring 进行简单使用,为了深入了解 Spring,我在 2020 年 6 月底的时候开始了 Spring 探索之路,并开设了《重学 Spring》专栏,到目前为止已经更新了 51 篇,内容涵盖了 Spring IOC、Spring AOP、Spring MVC 等内容,详细的介绍了 Spring 的核心特性与底层原理,也希望在读的小伙伴能更上一层楼。
76 0
5 分钟快速理解 Spring Boot