网上的教程大部分都是基于之前的版本,使用的是已经抛弃的继承WebSecurityConfigurerAdapter这个类进行SpringSecurity配置,但是新版本的SpringSecurity已经弃用了WebSecurityConfigurerAdapter这个类,以前的方法依旧可以使用,但是官方并不推荐使用这种方式
这点在官网也可以看到
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
官方文档说的是:
To assist with the transition to this new style of configuration, we have compiled a list of common use-cases and the suggested alternatives going forward.
In the examples below we follow best practice by using the Spring Security lambda DSL and the method HttpSecurity#authorizeHttpRequests
to define our authorization rules. If you are new to the lambda DSL you can read about it in this blog post. If you would like to learn more about why we choose to use HttpSecurity#authorizeHttpRequests
you can check out the reference documentation.
翻译过来就是
在 Spring Security 5.7.0-M2 中,我们弃用了WebSecurityConfigurerAdapter
,因为我们鼓励用户转向基于组件的安全配置为了帮助过渡到这种新的配置方式,我们编制了一份常见用例列表和建议的替代方案。
在下面的示例中,我们遵循最佳实践,使用 Spring Security lambda DSL 和方法HttpSecurity#authorizeHttpRequests
来定义我们的授权规则。如果您是 lambda DSL 的新手,您可以阅读这篇博文。如果您想详细了解我们选择使用HttpSecurity#authorizeHttpRequests
的原因,可以查看参考文档。
https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html
我依旧推荐大家去官网查看,看不懂可以用翻译。
在之前的版本中 我们更加常用的方式是覆盖重写configure(HttpSecurity http)方法,例如:
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and().formLogin();
}
}
而现版本推荐声明SecurityFilterChain而不是使用authorizeRequests,例如:
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated();
)
// ...
return http.build();
}
这里不多赘述原理 如果大家很感兴趣的话,我可以专门出一个专栏给大家讲解原理,从基础到新版本的区别
本文章更多在于配置
package org.zhang.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.zhang.comont.AliErrorCodeEnum;
import org.zhang.comont.ResultBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Result;
import java.io.IOException;
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, IOException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=UTF-8");
ResultBody error = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0111);
String s = new ObjectMapper().writeValueAsString(error);
response.getWriter().println(s);
}
}
package org.zhang.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.zhang.comont.AliErrorCodeEnum;
import org.zhang.comont.ResultBody;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class FailHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
ResultBody result = null;
if (e instanceof AccountExpiredException)
{
result = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0230);
}else if (e instanceof BadCredentialsException){
result = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0210);
}else if (e instanceof CredentialsExpiredException){
result = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0202);
}else if (e instanceof InternalAuthenticationServiceException){
result = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0201);
}else{
result = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0220);
}
response.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}
}
这里面的ReusltBody是我自定义的统一返回数据类型,大家可以百度,也可以使用Map简单放回
package org.zhang.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
//注销成功
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String, Object> result = new HashMap<String, Object>();
result.put("msg", "注销成功");
result.put("status", 200);
response.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}
}
package org.zhang.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.zhang.comont.AliErrorCodeEnum;
import org.zhang.comont.ResultBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Result;
import java.io.IOException;
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, IOException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=UTF-8");
ResultBody error = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0111);
String s = new ObjectMapper().writeValueAsString(error);
response.getWriter().println(s);
}
}
package org.zhang.config;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.ObjectUtils;
import org.zhang.utils.JwtUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//获取请求的Token
String token = request.getHeader("Authorization");
//没有直接跳过
if (ObjectUtils.isEmpty(token))
{
chain.doFilter(request,response);
return;
}
//将token中的用户名和权限用户组放入Authentication对象,在之后实现鉴权
SecurityContextHolder.getContext().setAuthentication(getAuthentication(token));
super.doFilterInternal(request, response, chain);
}
//解析token获取用户信息
private UsernamePasswordAuthenticationToken getAuthentication(String token)
{
HashMap<String, Object> tokenInfo = JwtUtils.decode(token);
if(ObjectUtils.isEmpty(tokenInfo)){
return null;
}
String username = (String) tokenInfo.get("username");
String[] roles = (String[]) tokenInfo.get("roles");
ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>();
for(String role:roles){
authorities.add(new SimpleGrantedAuthority(role));
}
return new UsernamePasswordAuthenticationToken(username,null,authorities);
}
}
7、重写登陆配置(为了解决前端传值为Json类型)
package org.zhang.config;
import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.zhang.pojo.User;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class LoginFilter extends UsernamePasswordAuthenticationFilter{
//登陆认证接口
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//判断是否为Post请求
if(!request.getMethod().equals("POST"))
{
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
//判断认证数据是否为Json 如果登陆数据为JSON 进行JSON处理
if(request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE))
{
Map<String,String> loginData = new HashMap<>();
try
{
//将Json数据转换为Map
loginData = new ObjectMapper().readValue(request.getInputStream(),Map.class);
}catch (IOException e)
{
throw new RuntimeException(e);
}
String username = loginData.get(getUsernameParameter());
String password = loginData.get(getPasswordParameter());
if (username == null)
{
throw new AuthenticationServiceException("用户名不能为空");
}
if (password == null)
{
throw new AuthenticationServiceException("密码不能为空");
}
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}else
{
//普通表单提交数据处理
String username = this.obtainUsername(request);
String password = this.obtainPassword(request);
if (username == null)
{
username = "";
}
if (password == null)
{
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,password);
this.setDetails(request,authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
}
package org.zhang.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.zhang.comont.AliErrorCodeEnum;
import org.zhang.comont.ResultBody;
import org.zhang.service.impl.UserServiceImpl;
import java.util.Arrays;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Autowired
private UserServiceImpl userService;
/**
* 获取AuthenticationManager(认证管理器),登录时认证使用
* @param AuthenticationConfiguration
* @return
* @throws Exception
*/
@Bean(name = "authenticationManager")
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz)->
authz
.antMatchers("/root/doLogin").permitAll()
.antMatchers("/user/userLogin").permitAll()
.antMatchers("/film/**").permitAll()
.antMatchers("/order/queryTodayMarket").permitAll()
.anyRequest().authenticated())
.logout(logout->
logout
.logoutSuccessHandler(new MyLogoutSuccessHandler()))
.cors(cors->
cors
.configurationSource(configurationSource()))
.httpBasic(withDefaults())
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()//异常处理
.authenticationEntryPoint((request, response, e) -> {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
ResultBody error = ResultBody.error("401", "尚未认证,请进行认证操作!");
response.getWriter().write(new ObjectMapper().writeValueAsString(error));
})
.accessDeniedHandler((request, response, e) -> {
response.setContentType("application/json;charset=UTF-8");
ResultBody s = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0301);
response.getWriter().write(new ObjectMapper().writeValueAsString(s));
})
.and()
.csrf().disable(); //前后端分离没有Session 不需要开启csrf
http.addFilterAt(loginFilter(http.getSharedObject(AuthenticationManager.class)),UsernamePasswordAuthenticationFilter.class);
http.addFilter(jwtAuthorizationFilter(http.getSharedObject(AuthenticationManager.class)));
return http.build();
}
//过滤静态资源
@Bean
public WebSecurityCustomizer webSecurityCustomizer(){
return (web)-> web.ignoring().antMatchers("/static/css");
}
// 注入登陆过滤器
@Bean
public LoginFilter loginFilter(AuthenticationManager authenticationManager) throws Exception {
LoginFilter loginFilter = new LoginFilter();
loginFilter.setFilterProcessesUrl("/root/doLogin");
loginFilter.setUsernameParameter("username");
loginFilter.setPasswordParameter("password");
loginFilter.setAuthenticationManager(authenticationManager);
loginFilter.setAuthenticationSuccessHandler(new SuccessHandler());
loginFilter.setAuthenticationFailureHandler(new FailHandler());
return loginFilter;
}
//加入token验证过滤器
@Bean
public JWTAuthorizationFilter jwtAuthorizationFilter(AuthenticationManager authenticationManager){
JWTAuthorizationFilter filter = new JWTAuthorizationFilter(authenticationManager);
return filter;
}
//springsecurity跨域
CorsConfigurationSource configurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
corsConfiguration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
}
```# SpringSecurity使用总结:
## 1、Springsecurity目前问题:
>网上的教程大部分都是基于之前的版本,使用的是已经抛弃的继承WebSecurityConfigurerAdapter这个类进行SpringSecurity配置,但是新版本的SpringSecurity已经弃用了WebSecurityConfigurerAdapter这个类,以前的方法依旧可以使用,但是官方并不推荐使用这种方式
>
>这点在官网也可以看到
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
官方文档说的是:
To assist with the transition to this new style of configuration, we have compiled a list of common use-cases and the suggested alternatives going forward.
In the examples below we follow best practice by using the Spring Security lambda DSL and the method `HttpSecurity#authorizeHttpRequests` to define our authorization rules. If you are new to the lambda DSL you can read about it in [this blog post](https://spring.io/blog/2019/11/21/spring-security-lambda-dsl). If you would like to learn more about why we choose to use `HttpSecurity#authorizeHttpRequests` you can check out the [reference documentation](https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html).
翻译过来就是
在 Spring Security 5.7.0-M2 中,我们[弃用](https://translate.google.com/website?sl=auto&tl=zh-CN&hl=zh-CN&client=webapp&u=https://github.com/spring-projects/spring-security/issues/10822)了`WebSecurityConfigurerAdapter`,因为我们鼓励用户转向基于组件的安全配置为了帮助过渡到这种新的配置方式,我们编制了一份常见用例列表和建议的替代方案。
在下面的示例中,我们遵循最佳实践,使用 Spring Security lambda DSL 和方法`HttpSecurity#authorizeHttpRequests`来定义我们的授权规则。如果您是 lambda DSL 的新手,您可以阅读[这篇博](https://spring-io.translate.goog/blog/2019/11/21/spring-security-lambda-dsl?_x_tr_sl=auto&_x_tr_tl=zh-CN&_x_tr_hl=zh-CN&_x_tr_pto=wapp)文。如果您想详细了解我们选择使用`HttpSecurity#authorizeHttpRequests`的原因,可以查看[参考文档](https://translate.google.com/website?sl=auto&tl=zh-CN&hl=zh-CN&client=webapp&u=https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html)。
### 1.1、什么是Spring Security lambda DSL ?
https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html
我依旧推荐大家去官网查看,看不懂可以用翻译。
在之前的版本中 我们更加常用的方式是覆盖重写configure(HttpSecurity http)方法,例如:
```java
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and().formLogin();
}
}
而现版本推荐声明SecurityFilterChain而不是使用authorizeRequests,例如:
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated();
)
// ...
return http.build();
}
这里不多赘述原理 如果大家很感兴趣的话,我可以专门出一个专栏给大家讲解原理,从基础到新版本的区别
本文章更多在于配置
package org.zhang.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.zhang.comont.AliErrorCodeEnum;
import org.zhang.comont.ResultBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Result;
import java.io.IOException;
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, IOException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=UTF-8");
ResultBody error = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0111);
String s = new ObjectMapper().writeValueAsString(error);
response.getWriter().println(s);
}
}
package org.zhang.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.zhang.comont.AliErrorCodeEnum;
import org.zhang.comont.ResultBody;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class FailHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
ResultBody result = null;
if (e instanceof AccountExpiredException)
{
result = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0230);
}else if (e instanceof BadCredentialsException){
result = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0210);
}else if (e instanceof CredentialsExpiredException){
result = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0202);
}else if (e instanceof InternalAuthenticationServiceException){
result = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0201);
}else{
result = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0220);
}
response.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}
}
这里面的ReusltBody是我自定义的统一返回数据类型,大家可以百度,也可以使用Map简单放回
package org.zhang.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
//注销成功
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String, Object> result = new HashMap<String, Object>();
result.put("msg", "注销成功");
result.put("status", 200);
response.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}
}
package org.zhang.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.zhang.comont.AliErrorCodeEnum;
import org.zhang.comont.ResultBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Result;
import java.io.IOException;
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, IOException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=UTF-8");
ResultBody error = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0111);
String s = new ObjectMapper().writeValueAsString(error);
response.getWriter().println(s);
}
}
package org.zhang.config;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.ObjectUtils;
import org.zhang.utils.JwtUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//获取请求的Token
String token = request.getHeader("Authorization");
//没有直接跳过
if (ObjectUtils.isEmpty(token))
{
chain.doFilter(request,response);
return;
}
//将token中的用户名和权限用户组放入Authentication对象,在之后实现鉴权
SecurityContextHolder.getContext().setAuthentication(getAuthentication(token));
super.doFilterInternal(request, response, chain);
}
//解析token获取用户信息
private UsernamePasswordAuthenticationToken getAuthentication(String token)
{
HashMap<String, Object> tokenInfo = JwtUtils.decode(token);
if(ObjectUtils.isEmpty(tokenInfo)){
return null;
}
String username = (String) tokenInfo.get("username");
String[] roles = (String[]) tokenInfo.get("roles");
ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>();
for(String role:roles){
authorities.add(new SimpleGrantedAuthority(role));
}
return new UsernamePasswordAuthenticationToken(username,null,authorities);
}
}
7、重写登陆配置(为了解决前端传值为Json类型)
package org.zhang.config;
import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.zhang.pojo.User;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class LoginFilter extends UsernamePasswordAuthenticationFilter{
//登陆认证接口
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//判断是否为Post请求
if(!request.getMethod().equals("POST"))
{
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
//判断认证数据是否为Json 如果登陆数据为JSON 进行JSON处理
if(request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE))
{
Map<String,String> loginData = new HashMap<>();
try
{
//将Json数据转换为Map
loginData = new ObjectMapper().readValue(request.getInputStream(),Map.class);
}catch (IOException e)
{
throw new RuntimeException(e);
}
String username = loginData.get(getUsernameParameter());
String password = loginData.get(getPasswordParameter());
if (username == null)
{
throw new AuthenticationServiceException("用户名不能为空");
}
if (password == null)
{
throw new AuthenticationServiceException("密码不能为空");
}
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}else
{
//普通表单提交数据处理
String username = this.obtainUsername(request);
String password = this.obtainPassword(request);
if (username == null)
{
username = "";
}
if (password == null)
{
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,password);
this.setDetails(request,authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
}
package org.zhang.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.zhang.comont.AliErrorCodeEnum;
import org.zhang.comont.ResultBody;
import org.zhang.service.impl.UserServiceImpl;
import java.util.Arrays;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Autowired
private UserServiceImpl userService;
/**
* 获取AuthenticationManager(认证管理器),登录时认证使用
* @param AuthenticationConfiguration
* @return
* @throws Exception
*/
@Bean(name = "authenticationManager")
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz)->
authz
.antMatchers("/root/doLogin").permitAll()
.antMatchers("/user/userLogin").permitAll()
.antMatchers("/film/**").permitAll()
.antMatchers("/order/queryTodayMarket").permitAll()
.anyRequest().authenticated())
.logout(logout->
logout
.logoutSuccessHandler(new MyLogoutSuccessHandler()))
.cors(cors->
cors
.configurationSource(configurationSource()))
.httpBasic(withDefaults())
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()//异常处理
.authenticationEntryPoint((request, response, e) -> {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
ResultBody error = ResultBody.error("401", "尚未认证,请进行认证操作!");
response.getWriter().write(new ObjectMapper().writeValueAsString(error));
})
.accessDeniedHandler((request, response, e) -> {
response.setContentType("application/json;charset=UTF-8");
ResultBody s = ResultBody.error(AliErrorCodeEnum.USER_ERROR_A0301);
response.getWriter().write(new ObjectMapper().writeValueAsString(s));
})
.and()
.csrf().disable(); //前后端分离没有Session 不需要开启csrf
http.addFilterAt(loginFilter(http.getSharedObject(AuthenticationManager.class)),UsernamePasswordAuthenticationFilter.class);
http.addFilter(jwtAuthorizationFilter(http.getSharedObject(AuthenticationManager.class)));
return http.build();
}
//过滤静态资源
@Bean
public WebSecurityCustomizer webSecurityCustomizer(){
return (web)-> web.ignoring().antMatchers("/static/css");
}
// 注入登陆过滤器
@Bean
public LoginFilter loginFilter(AuthenticationManager authenticationManager) throws Exception {
LoginFilter loginFilter = new LoginFilter();
loginFilter.setFilterProcessesUrl("/root/doLogin");
loginFilter.setUsernameParameter("username");
loginFilter.setPasswordParameter("password");
loginFilter.setAuthenticationManager(authenticationManager);
loginFilter.setAuthenticationSuccessHandler(new SuccessHandler());
loginFilter.setAuthenticationFailureHandler(new FailHandler());
return loginFilter;
}
//加入token验证过滤器
@Bean
public JWTAuthorizationFilter jwtAuthorizationFilter(AuthenticationManager authenticationManager){
JWTAuthorizationFilter filter = new JWTAuthorizationFilter(authenticationManager);
return filter;
}
//springsecurity跨域
CorsConfigurationSource configurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
corsConfiguration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
}