前边我们安装了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的基本使用。
有好的建议,请在下方输入你的评论。