原文档位于有道云中,可以更好的浏览
文档:spring-security使用.note
链接:http://note.youdao.com/noteshare?id=56c0fc9382b5dc19f69f6874adfaf905&sub=5086D29C26684C14B769F1AD14529C50
一、 相关概念说明
1、Spring Security
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
2、HttpBasic
a、首先,HttpBasic模式要求传输的用户名密码使用Base64模式进行加密。如果用户名是 “admin” ,密码是“ admin”,则将字符串"admin:admin"使用Base64编码算法加密。加密结果可能是:YWtaW46YWRtaW4=。
b、然后,在Http请求中使用Authorization作为一个Header,“Basic YWtaW46YWRtaW4=“作为Header的值,发送给服务端。(注意这里使用Basic+空格+加密串)
c、服务器在收到这样的请求时,到达BasicAuthenticationFilter过滤器,将提取“ Authorization”的Header值,并使用用于验证用户身份的相同算法Base64进行解码。
d、解码结果与登录验证的用户名密码匹配,匹配成功则可以继续过滤器后续的访问。
所以,HttpBasic模式真的是非常简单又简陋的验证模式,Base64的加密算法是可逆的,你知道上面的原理,分分钟就破解掉。我们完全可以使用PostMan工具,发送Http请求进行登录验证。
二、实现过程
1、配置用户名与密码:
security.basic.basic-user-info[0].username=test1
security.basic.basic-user-info[0].password=abc123
security.basic.basic-user-info[0].roles[0]=ADMIN
security.basic.basic-user-info[0].roles[1]=USER
security.basic.basic-user-info[1].username=test2
security.basic.basic-user-info[1].password=abc123
security.basic.basic-user-info[1].roles[0]=USER
自定义对应的用户类
package com.sitech.gsgc.security.model;
import lombok.Data;
import lombok.ToString;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.util.*;
/**
@author: renzj
@date: 2022-06-16
@description: security认证的信息对象
@version: 1.0
*/
@Data
@ToString
public class SecurityUserInfo {
private String username;
private String password;
private String[] roles;
}
添加从配置中获取内容
package com.sitech.gsgc.security.model;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
@author: renzj
@date: 2022-06-16
@description: 从配置文件中获取配置的用户名与密码等信息
@version: 1.0
*/
@Component
@ConfigurationProperties(prefix = “security.basic”)
public class SecurityBasicProperties {
private List securityUserInfos;
public List getSecurityUserInfo() {
return securityUserInfos;
}
public void setBasicUserInfo(List basicUserInfo) {
this.securityUserInfos = basicUserInfo;
}
}
2、拦截配置类
package com.sitech.gsgc.security.basic;
import com.sitech.gsgc.security.model.SecurityBasicProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
/**
@author: renzj
@date: 2022-06-20
@description: spring-security配置类,
@version: 1.0
*/
@EnableWebSecurity
@Configuration
@Profile(“secure-basic-web”)
// @EnableConfigurationProperties(SecurityBasicProperties.class)
public class GsgcWebSecurityConfig extends WebSecurityConfigurerAdapter {
public static String REALM = “BASIC_GSGC”;
//用来指定从何处获取用户
@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(getUserDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
}
//注入自定义的用户认证逻辑
@Bean
public UserDetailsService getUserDetailsService() {
return new GsgcUserDetailsService();
}
//开启跨域
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.applyPermitDefaultValues();
configuration.addAllowedMethod(“*”);
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration(“/**”, configuration);
return source;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//csrf是防伪造攻击,如果开启则我们没办法用postman来测试,必须关掉
http.csrf().disable()
//所有方式的请求都要通过验证
.authorizeRequests()
//放行security验证接口
.antMatchers(“/testSecurity”).permitAll()
// .antMatchers(“/upload”).permitAll()
.anyRequest().hasRole(“ADMIN”)
//添加自定义的403返回内容
.and().exceptionHandling().accessDeniedHandler(myAccessDeniedHandler())
//添加自定义的401返回内容
.and().httpBasic().realmName(REALM).authenticationEntryPoint(getBasicAuthEntryPoint())
//关闭session
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);//We don’t need sessions to be created.
}
@Bean
public GsgcAuthenticationEndPoint getBasicAuthEntryPoint() {
return new GsgcAuthenticationEndPoint();
}
@Bean
public GsgcAccessDeniedHandler myAccessDeniedHandler() {
return new GsgcAccessDeniedHandler();
}
}
3、自定义异常类
401异常类
package com.sitech.gsgc.security.basic;
import cn.hutool.json.JSONUtil;
import com.sitech.gsgc.service.support.ServerResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
@author renzj
@version 1.0
@date 2020-09-24 10:36
针对没登录的请求到来进行统一处理
*/
public class GsgcAuthenticationEndPoint extends BasicAuthenticationEntryPoint {
@Override
public void commence(final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException) throws IOException {
//Authentication failed, send error response.
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.addHeader(“WWW-Authenticate”, “Basic realm=” + getRealmName() + “”);
// PrintWriter writer = response.getWriter();
// writer.println("HTTP Status 401 : " + authException.getMessage());
response.setContentType(“application/json;charset=utf-8”);
ServerResponse error = ServerResponse.createError(“HTTP Status 401:”+authException.getMessage());
String jsonStr = JSONUtil.toJsonStr(error);
response.getWriter().write(jsonStr);
response.getWriter().flush();
}
@Override
public void afterPropertiesSet() throws Exception {
setRealmName(GsgcWebSecurityConfig.REALM);
super.afterPropertiesSet();
}
}
403异常类
package com.sitech.gsgc.security.basic;
import cn.hutool.json.JSONUtil;
import com.sitech.gsgc.service.support.ServerResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
}
4、自定义用户验证过程
package com.sitech.gsgc.security.basic;
import com.sitech.gsgc.security.model.SecurityBasicProperties;
import com.sitech.gsgc.security.model.SecurityUserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.ObjectUtils;
import java.util.*;
/**
@author: renzj
@date: 2022-06-16
@description: 继承security中的用户密码验证规则,实现自己的逻辑
@version: 1.0
*/
public class GsgcUserDetailsService implements UserDetailsService {
//获取配置信息中的用户信息
@Autowired
private SecurityBasicProperties basicProperties;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails userDetails = null;
List securityUserInfo = basicProperties.getSecurityUserInfo();
//利用并行流处理数据
Optional first = securityUserInfo.parallelStream().filter(item -> item.getUsername().equals(username)).findFirst();
//当验证没有当前用户时,提示报错
if (!first.isPresent()) {
throw new BadCredentialsException(“User '” + username + “’ not found”);
}
SecurityUserInfo userInfo = first.get();
Collection authList = getAuthorities(userInfo.getRoles());
userDetails = new User(userInfo.getUsername(), new BCryptPasswordEncoder().encode(userInfo.getPassword()), true, true, true, true, authList);
return userDetails;
}
private Collection getAuthorities(String[] roles) {
List authList = new ArrayList();
if (ObjectUtils.isEmpty(roles) || roles.length == 0) {
authList.add(new SimpleGrantedAuthority(“ROLE_USER”));
} else {
Arrays.stream(roles).forEach(role -> {
authList.add(new SimpleGrantedAuthority(“ROLE_” + role));
});
}
return authList;
}
}
5、测试样例
调用成功
{
“resCode”: “0000”,
“resMsg”: “The test Result is{“resCode”:“0000”,“resMsg”:“The gsgc application is healthy”}”
}
调用401
{
“resCode”: “0001”,
“resMsg”: “HTTP Status 401:Bad credentials”
}
调用403
{
“resCode”: “0001”,
“resMsg”: “HTTP Status 403:Access is denied”
}
用户验证异常
{
“resCode”: “0001”,
“resMsg”: “HTTP Status 401:Invalid basic authentication token”
}