SpringBoot(三十四)SpringBoot集成Security

246 阅读8分钟

前边我们安装了actuator程序监控器,这个程序监控器得网址是开放访问的。那不行啊,这玩意怎么能让其他人随便访问呢?

 

那得想个办法。

 

Springboot是有正经的安全框架的,分别是shiro和Secuity,shiro功能相对简单,Secuity功能相对复杂。

 

我这里在项目中集成一下secuity框架。

 

一:添加pom依赖

<!-- 安全框架       -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

 

二:添加application.yml配置

spring:
  # security安全框架
  security:
    user:
      name: xxxx
      password: xxxx

 

三:添加config配置类

package com.modules.config;

import com.modules.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.http.HttpStatus;

import java.io.PrintWriter;

/**
 * @author secuity配置类
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{

    //注入Jwt认证拦截器.(这里边没有什么实际内容,我得jwt拦截没有配置在这里)
    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /**
     * 将BCryptPasswordEncoder加密器注入SpringSecurity中
     * 之后SpringSecurity的DaoAuthenticaionProvider会调用该加密器中的match()方法进行密码比对,
     * 密码比对过程不需要我们干涉
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder()
    {
        // 使用BCrypt算法加密密码
        return new BCryptPasswordEncoder();
        // 密码不加密
        //return NoOpPasswordEncoder.getInstance();
    }

    /*@Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception
    {
        // 身份验证管理器, 直接继承即可.
        return super.authenticationManagerBean();
    }//*/

    /**
     * 配置认证规则
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        // =================================================
        // 本地演示使用,正常都是从数据库里取值
        auth
            .inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin")// 角色名称
                .password(new BCryptPasswordEncoder().encode("admin"))  // 角色密码
                .roles("ADMIN","USER") //对应角色(可以一对多)
            .and()
                .withUser("user")// 角色名称
                .password(new BCryptPasswordEncoder().encode("user"))  // 角色密码
                .roles("USER"); //对应角色
    }

    /**
     * 配置忽略静态资源:在配置类中重写configure()方法,配置忽略静态资源的访问限制。
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception
    {
        // 继承 父类配置
        // super.configure(web);
        web
            .ignoring()
            .antMatchers("/css/**", "/js/**", "/images/**");
    }

    /**
     * 配置授权规则:在配置类中重写configure()方法,配置认证和授权规则。
     * @param httpSecurity
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception
    {
        httpSecurity
            .authorizeRequests()    //限定签名成功的请求
                .antMatchers("/actuator/**")//actuator 下的接口,需要 ADMIN 权限
                .hasRole("ADMIN")
                .antMatchers("/mysql/**")//mysql 下的接口,需要 user 权限
                .hasRole("USER")    // .hasAnyRole("MEMBER","ADMIN")
                .antMatchers("/java/**").permitAll()    // 允许访问的接口
                .anyRequest()   //其他没有限定的请求,允许访问
                .authenticated()   // 其他所有请求需要身份验证
            .and()
                .httpBasic()    //启用http 基础验证
            .and()
                .anonymous()    //对于没有配置权限的其他请求允许匿名访问
            .and()
                .formLogin()    // 跳转登录页面
                //.loginPage("/login") // 自定义登录页面(美观)
                .permitAll()  // 放行
            .and()
                .logout()   // 退出登录
                .deleteCookies("remember-me")   // 记住我
                .permitAll()    // 放行
            .and()
                .rememberMe()   // 记住我
                .tokenValiditySeconds(3600 * 24 * 7)    // 有效期7天
                .rememberMeParameter("remember-me")     // 开启记住我功能
            .and()
                .csrf().disable()// 关闭CSRF
                // header response的X-Frame-Options属性设置为SAMEORIGIN
                .headers().frameOptions().sameOrigin()
            .and()
                .cors();     //允许跨域请求

        //把token校验过滤器添加到过滤器链中, 添加在UsernamePasswordAuthenticationFilter之前是因为只要用户携带token, 就不需要再去验证是否有用户名密码了 (而且我们不使用表单登入, UsernamePasswordAuthenticationFilter是无法解析Json的, 相当于它没用了)
        //UsernamePasswordAuthenticationFilter是SpringSecurity默认配置的表单登录拦截器
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        // 下面这个是权限拒绝处理器(返回json), 这个直接照搬就行了.
        /*httpSecurity.exceptionHandling(it -> it.authenticationEntryPoint(((httpServletRequest, httpServletResponse, e) -> {
            String msg = "{"code": "-1000","msg": "You do not have permission to access!"}";
            httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value());
            httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
            PrintWriter writer = httpServletResponse.getWriter();
            writer.write(msg);
            writer.flush();
            writer.close();
        })));//*/
    }
}

 

参数详解

1、注解 @EnableWebSecurity

在 SpringBoot 应用中使用 Spring Security,用到了 @EnableWebSecurity 注解,官方说明为,该注解和 @Configuration 注解一起使用,注解 WebSecurityConfigurer 类型的类,或者利用@EnableWebSecurity 注解继承 WebSecurityConfigurerAdapter的类,这样就构成了 Spring Security 的配置。

 

2、抽象类 WebSecurityConfigurerAdapter

一般情况,会选择继承 WebSecurityConfigurerAdapter 类,其官方说明为:WebSecurityConfigurerAdapter 提供了一种便利的方式去创建 WebSecurityConfigurer的实例,只需要重写 WebSecurityConfigurerAdapter 的方法,即可配置拦截什么URL、设置什么权限等安全控制。

 

3、方法 configure(AuthenticationManagerBuilder auth) 认证 和 configure(HttpSecurity http) 授权

 

4:、final 类 HttpSecurity

HttpSecurity 常用方法及说明:

openidLogin()用于基于 OpenId 的验证
headers()将安全标头添加到响应
cors()配置跨域资源共享( CORS )
sessionManagement()允许配置会话管理
portMapper()允许配置一个PortMapper(HttpSecurity#(getSharedObject(class))),其他提供SecurityConfigurer的对象使用 PortMapper 从 HTTP 重定向到 HTTPS 或者从 HTTPS 重定向到 HTTP。默认情况下,Spring Security使用一个PortMapperImpl映射 HTTP 端口8080到   HTTPS 端口8443,HTTP 端口80到 HTTPS 端口443
jee()配置基于容器的预认证。 在这种情况下,认证由Servlet容器管理
x509()配置基于x509的认证
rememberMe允许配置“记住我”的验证
authorizeRequests()允许基于使用HttpServletRequest限制访问
requestCache()允许配置请求缓存
exceptionHandling()允许配置错误处理
securityContext()在HttpServletRequests之间的SecurityContextHolder上设置SecurityContext的管理。   当使用WebSecurityConfigurerAdapter时,这将自动应用   
servletApi()将HttpServletRequest方法与在其上找到的值集成到SecurityContext中。 当使用WebSecurityConfigurerAdapter时,这将自动应用
csrf()添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用
logout()添加退出登录支持。当使用WebSecurityConfigurerAdapter时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效来清除用户,清除已配置的任何#rememberMe()身份验证,清除SecurityContextHolder,然后重定向到”/login?success”
anonymous()允许配置匿名用户的表示方法。 当与WebSecurityConfigurerAdapter结合使用时,这将自动应用。     默认情况下,匿名用户将使用org.springframework.security.authentication.AnonymousAuthenticationToken表示,并包含角色 “ROLE_ANONYMOUS”
formLogin()指定支持基于表单的身份验证。如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面
oauth2Login()根据外部OAuth 2.0或OpenID   Connect 1.0提供程序配置身份验证
requiresChannel()配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射
httpBasic()配置 Http Basic 验证
addFilterAt()在指定的Filter类的位置添加过滤器

 

四:解析一下上方的代码

上方的代码中都是有注释的,其实参照一下就可以理解,但是,为了我以后自己不要忘了,我在这里记录一下我的理解。

 

1:设置密码加密方式

/**
 * 将BCryptPasswordEncoder加密器注入SpringSecurity中
 * 之后SpringSecurity的DaoAuthenticaionProvider会调用该加密器中的match()方法进行密码比对,
 * 密码比对过程不需要我们干涉
 * @return
 */
@Bean
public PasswordEncoder passwordEncoder()
{
    // 使用BCrypt算法加密密码(要与认证方法中密码加密的方法一致)
    return new BCryptPasswordEncoder();
    // 密码不加密
    //return NoOpPasswordEncoder.getInstance();
}

 

2:配置认证规则

/**
 * 配置认证规则
 * @param auth
 * @throws Exception
 */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
    // ==================================================
    // 本地演示使用,正常都是从数据库里取值
    auth
        .inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
            .withUser("admin")// 角色名称
            .password(new BCryptPasswordEncoder().encode("admin"))  // 角色密码
            .roles("ADMIN","USER"//对应角色(可以一对多)
        .and()
            .withUser("user")// 角色名称
            .password(new BCryptPasswordEncoder().encode("user"))  // 角色密码
            .roles("USER"); //对应角色
}

configure(AuthenticationManagerBuilder auth)配置认证规则。

我上边配置了两个用户admin/user。Admin用户对应两个角色,ADMIN/USER,这里说明用户对应角色可以一对多。

 

3:配置忽略静态资源

/**
 * 配置忽略静态资源:在配置类中重写configure()方法,配置忽略静态资源的访问限制。
 * @param web
 * @throws Exception
 */
@Override
public void configure(WebSecurity web) throws Exception
{
    // 继承 父类配置
    // super.configure(web);
    web
        .ignoring()
        .antMatchers("/css/**""/js/**""/images/**");
}

这部分没什么好讲的,对我来说是没有什么用的,我前端使用的是VUE,springboot框架只做接口使用。

 

4:配置权限

/**
 * 配置授权规则:在配置类中重写configure()方法,配置认证和授权规则。
 * @param httpSecurity
 * @throws Exception
 */
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
    httpSecurity
        .authorizeRequests()    //限定签名成功的请求
            .antMatchers("/actuator/**")//actuator 下的接口,需要 ADMIN 权限
            .hasRole("ADMIN")
            .antMatchers("/mysql/**")//mysql 下的接口,需要 user 权限
            .hasRole("USER")    // .hasAnyRole("MEMBER","ADMIN")
            .antMatchers("/java/**").permitAll()    // 允许访问的接口
            .anyRequest()   //其他没有限定的请求,允许访问
            .authenticated()   // 其他所有请求需要身份验证
        .and()
            .httpBasic()    //启用http 基础验证
        .and()
            .anonymous()    //对于没有配置权限的其他请求允许匿名访问
        .and()
            .formLogin()    // 跳转登录页面
            //.loginPage("/login") // 自定义登录页面(美观)
            .permitAll()  // 放行
        .and()
            .logout()   // 退出登录
            .deleteCookies("remember-me")   // 记住我
            .permitAll()    // 放行
        .and()
            .rememberMe()   // 记住我
            .tokenValiditySeconds(3600 * 24 * 7)    // 有效期7天
            .rememberMeParameter("remember-me")     // 开启记住我功能
        .and()
            .csrf().disable()// 关闭CSRF
            // header response的X-Frame-Options属性设置为SAMEORIGIN
            .headers().frameOptions().sameOrigin()
        .and()
            .cors();     //允许跨域请求

    //把token校验过滤器添加到过滤器链中, 添加在UsernamePasswordAuthenticationFilter之前是因为只要用户携带token, 就不需要再去验证是否有用户名密码了 (而且我们不使用表单登入, UsernamePasswordAuthenticationFilter是无法解析Json的, 相当于它没用了)
    //UsernamePasswordAuthenticationFilter是SpringSecurity默认配置的表单登录拦截器
    httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

    // 下面这个是权限拒绝处理器(返回json), 这个直接照搬就行了.
    /*httpSecurity.exceptionHandling(it -> it.authenticationEntryPoint(((httpServletRequest, httpServletResponse, e) -> {
        String msg = "{"code": "-1000","msg": "You do not have permission to access!"}";
        httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value());
        httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
        PrintWriter writer = httpServletResponse.getWriter();
        writer.write(msg);
        writer.flush();
        writer.close();
    })));//*/
}

这部分是权限配置的主要部分。对应的方法中都是有注释的,参照即可。

 

运行项目,访问http://localhost:9091/actuator

使用user用户登录,是可以登录的,但是登录之后没有权限,报403错误

上方配置的这个网址登录用户权限只能是ADMIN,ADMIN角色用户只有一个admin用户

 

运行项目,访问http://localhost:7001/mysql/druid/login.html

使用user/admin用户登录都没有问题,上方配置的这个网址登录用户权限只能是USER,USER角色用户有两个,分别是user/admin用户

 

以上大概就是Springboot集成secuity的基本使用。

 

有好的建议,请在下方输入你的评论。