精通 Spring Boot 系列文(12)

程序IT圈

共 4632字,需浏览 10分钟

 · 2020-07-29

阅读全文,约 18 分钟


精通 Spring Boot 系列文(1)
精通 Spring Boot 系列文(2)
精通 Spring Boot 系列文(2)
精通 Spring Boot 系列文(4)
精通 Spring Boot 系列文(5)
精通 Spring Boot 系列文(6)
精通 Spring Boot 系列文(7)
精通 Spring Boot 系列文(8)
精通 Spring Boot 系列文(9)
精通 Spring Boot 系列文(10)
精通 Spring Boot 系列文(11)

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.0modelVersion>

    <groupId>com.nxgroupId>
    <artifactId>springbootdataartifactId>
    <version>1.0-SNAPSHOTversion>

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.2.6.RELEASEversion>
        <relativePath/>
    parent>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <java.version>1.8java.version>
    properties>

  <dependencies>

      
      <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>

      
      <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>

    
      <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-securityartifactId>
    dependency>

    <dependency>
      <groupId>junitgroupId>
      <artifactId>junitartifactId>
      <scope>testscope>
    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 authorities = authentication.getAuthorities();

        List roles = new ArrayList();

        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 roles) {
        if (roles.contains("ROLE_USER")) {
            return true;
        }
        return false;
    }

    private boolean isAdmin(List 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 roles = new ArrayList();

        for (GrantedAuthority a : authentication.getAuthorities()) {
            roles.add(a.getAuthority());
        }
        System.out.println("role = " + roles);
        return roles.toString();
    }
}

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

未完待续,等我下一篇 ~~~

Java后端编程

更多Java推文,关注公众号

浏览 37
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报