|
|
|
|
@ -1,16 +1,19 @@ |
|
|
|
|
package edu.ncst.award.config; |
|
|
|
|
|
|
|
|
|
import cn.hutool.extra.spring.SpringUtil; |
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper; |
|
|
|
|
import edu.ncst.award.config.security.DataBaseUrlVoter; |
|
|
|
|
import edu.ncst.award.config.security.CustomUserCache; |
|
|
|
|
import edu.ncst.award.config.security.filter.AuthCheckFilter; |
|
|
|
|
import edu.ncst.award.config.security.filter.LoginFilter; |
|
|
|
|
import edu.ncst.award.exception.CodeMsg; |
|
|
|
|
import edu.ncst.award.service.ILoginLogService; |
|
|
|
|
import edu.ncst.award.service.impl.system.UserServiceImpl; |
|
|
|
|
import edu.ncst.award.utils.ResultUtils; |
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
|
import lombok.RequiredArgsConstructor; |
|
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
|
import org.springframework.context.annotation.Bean; |
|
|
|
|
import org.springframework.context.annotation.Configuration; |
|
|
|
|
import org.springframework.data.redis.cache.RedisCache; |
|
|
|
|
import org.springframework.data.redis.core.RedisTemplate; |
|
|
|
|
import org.springframework.security.access.AccessDecisionManager; |
|
|
|
|
import org.springframework.security.access.AccessDecisionVoter; |
|
|
|
|
@ -26,8 +29,7 @@ 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.config.http.SessionCreationPolicy; |
|
|
|
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
|
|
|
|
import org.springframework.security.crypto.password.PasswordEncoder; |
|
|
|
|
import org.springframework.security.core.userdetails.cache.SpringCacheBasedUserCache; |
|
|
|
|
import org.springframework.security.web.access.expression.WebExpressionVoter; |
|
|
|
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
|
|
|
|
|
|
|
|
|
@ -39,25 +41,23 @@ import java.util.List; |
|
|
|
|
* 安全配置 |
|
|
|
|
*/ |
|
|
|
|
@Configuration |
|
|
|
|
@Slf4j |
|
|
|
|
@EnableWebSecurity |
|
|
|
|
@EnableGlobalMethodSecurity(securedEnabled=true) |
|
|
|
|
@RequiredArgsConstructor |
|
|
|
|
@EnableGlobalMethodSecurity(securedEnabled = true) |
|
|
|
|
public class SecurityConfig extends WebSecurityConfigurerAdapter { |
|
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
|
private UserServiceImpl userService; |
|
|
|
|
@Autowired |
|
|
|
|
private ILoginLogService loginLogService; |
|
|
|
|
@Autowired |
|
|
|
|
private DataBaseUrlVoter dataBaseUrlVoter; |
|
|
|
|
@Autowired |
|
|
|
|
private RedisTemplate<String, String> redisCacheTemplate; |
|
|
|
|
private final UserServiceImpl userService; |
|
|
|
|
private final ILoginLogService loginLogService; |
|
|
|
|
private final DataBaseUrlVoter dataBaseUrlVoter; |
|
|
|
|
private final RedisTemplate<String, String> redisCacheTemplate; |
|
|
|
|
/** |
|
|
|
|
* 认证失败处理类 |
|
|
|
|
*/ |
|
|
|
|
@Autowired |
|
|
|
|
CustomAccessDeniedHandler accessDeniedHandler; |
|
|
|
|
private final CustomAccessDeniedHandler accessDeniedHandler; |
|
|
|
|
|
|
|
|
|
//org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 解决 无法直接注入 AuthenticationManager |
|
|
|
|
* |
|
|
|
|
@ -66,17 +66,18 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { |
|
|
|
|
*/ |
|
|
|
|
@Bean |
|
|
|
|
@Override |
|
|
|
|
public AuthenticationManager authenticationManagerBean() throws Exception |
|
|
|
|
{ |
|
|
|
|
public AuthenticationManager authenticationManagerBean() throws Exception { |
|
|
|
|
return super.authenticationManagerBean(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 权限白名单 |
|
|
|
|
* 权限白名单' |
|
|
|
|
* ' |
|
|
|
|
* <p> |
|
|
|
|
* 注意:该名单下匹配的 URL 不会经过鉴权,因此也无法获取当前用户的信息 |
|
|
|
|
*/ |
|
|
|
|
private final String[] WHITE_LIST = { |
|
|
|
|
"/v2/api-docs", |
|
|
|
|
"/v3/api-docs", |
|
|
|
|
"/configuration/ui", |
|
|
|
|
"/swagger-resources/**", |
|
|
|
|
"/configuration/**", |
|
|
|
|
@ -95,20 +96,18 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 权限投票访问决策管理器 |
|
|
|
|
*/ |
|
|
|
|
@Bean |
|
|
|
|
public AccessDecisionManager accessDecisionManager() { |
|
|
|
|
List<AccessDecisionVoter<? extends Object>> decisionVoters |
|
|
|
|
List<AccessDecisionVoter<?>> decisionVoters |
|
|
|
|
= Arrays.asList( |
|
|
|
|
new WebExpressionVoter(), |
|
|
|
|
new RoleVoter(),//主要用来判断当前请求是否具备该接口所需要的角色
|
|
|
|
|
dataBaseUrlVoter, |
|
|
|
|
new AuthenticatedVoter()); |
|
|
|
|
return new UnanimousBased(decisionVoters);//要求所有 AccessDecisionVoter 均返回肯定的结果时,才代表授予权限。
|
|
|
|
|
return new UnanimousBased(decisionVoters);//要求所有 AccessDecisionVoter 一票否决,所有弃权时deny
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
@ -116,7 +115,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { |
|
|
|
|
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); |
|
|
|
|
daoAuthenticationProvider.setHideUserNotFoundExceptions(false); |
|
|
|
|
daoAuthenticationProvider.setUserDetailsService(userService); |
|
|
|
|
daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder()); |
|
|
|
|
daoAuthenticationProvider.setUserCache(new CustomUserCache(redisCacheTemplate)); |
|
|
|
|
daoAuthenticationProvider.setPasswordEncoder(CustomDelegatingPasswordEncoder.createDelegatingPasswordEncoder()); |
|
|
|
|
auth.authenticationProvider(daoAuthenticationProvider); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -135,68 +135,73 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { |
|
|
|
|
* rememberMe | 允许通过remember-me登录的用户访问 |
|
|
|
|
* authenticated | 用户登录后可访问 |
|
|
|
|
*/ |
|
|
|
|
//权限验证 基于资源配置
|
|
|
|
|
// HttpSecurity 及WebSecurity 作用是不一样的:
|
|
|
|
|
// WebSecurity 主要针对的全局的忽略规则,
|
|
|
|
|
// HttpSecurity主要是权限控制规则。
|
|
|
|
|
// @Override
|
|
|
|
|
// protected void configure(HttpSecurity http) throws Exception {
|
|
|
|
|
//
|
|
|
|
|
// //允许跨域
|
|
|
|
|
// http.cors().and().anonymous().and()
|
|
|
|
|
// // CRSF禁用,因为不使用session
|
|
|
|
|
// .csrf().disable()
|
|
|
|
|
// .formLogin().disable().httpBasic().disable()
|
|
|
|
|
// .authorizeRequests()
|
|
|
|
|
// .accessDecisionManager(accessDecisionManager())//访问决策管理器
|
|
|
|
|
// .antMatchers("/api/login").permitAll()
|
|
|
|
|
// .antMatchers("/system/init").permitAll()
|
|
|
|
|
// // 除上面外的所有请求全部需要鉴权认证
|
|
|
|
|
// .anyRequest().authenticated()
|
|
|
|
|
// .and().exceptionHandling().accessDeniedHandler(accessDeniedHandler).and()
|
|
|
|
|
// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
|
|
|
|
//
|
|
|
|
|
// http.addFilterAfter(new AuthCheckFilter(redisCacheTemplate), UsernamePasswordAuthenticationFilter.class)
|
|
|
|
|
// .addFilterAt(
|
|
|
|
|
// new LoginFilter(authenticationManager(), loginLogService, redisCacheTemplate), UsernamePasswordAuthenticationFilter.class
|
|
|
|
|
// )
|
|
|
|
|
// .exceptionHandling()//登录认证失败
|
|
|
|
|
// .authenticationEntryPoint(((request, response, authException) -> {
|
|
|
|
|
// response.setCharacterEncoding("utf-8");
|
|
|
|
|
// response.setContentType("application/json;charset=utf-8");
|
|
|
|
|
//
|
|
|
|
|
// PrintWriter out = response.getWriter();
|
|
|
|
|
// out.write(new ObjectMapper().writeValueAsString(ResultUtils.error(CodeMsg.USER_NEED_LOGIN)));
|
|
|
|
|
// out.flush();
|
|
|
|
|
// out.close();
|
|
|
|
|
// }));
|
|
|
|
|
//
|
|
|
|
|
// // 退出登录
|
|
|
|
|
// http.logout()
|
|
|
|
|
// .logoutUrl("/logout")
|
|
|
|
|
// .deleteCookies("JSESSIONID")
|
|
|
|
|
// .logoutSuccessHandler(((request, response, authentication) -> {
|
|
|
|
|
// PrintWriter out = response.getWriter();
|
|
|
|
|
// response.setContentType("application/json");
|
|
|
|
|
// out.write(new ObjectMapper().writeValueAsString(ResultUtils.success("注销成功")));
|
|
|
|
|
// out.flush();
|
|
|
|
|
// out.close();
|
|
|
|
|
// }));
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 权限验证 基于资源配置
|
|
|
|
|
// HttpSecurity 及WebSecurity 作用是不一样的:
|
|
|
|
|
// WebSecurity 主要针对的全局的忽略规则,
|
|
|
|
|
// HttpSecurity主要是权限控制规则。
|
|
|
|
|
@Override |
|
|
|
|
protected void configure(HttpSecurity httpSecurity) throws Exception{ |
|
|
|
|
//调试阶段
|
|
|
|
|
httpSecurity.csrf().disable().authorizeRequests(); |
|
|
|
|
httpSecurity.authorizeRequests().anyRequest() |
|
|
|
|
.permitAll().and().logout().permitAll(); |
|
|
|
|
protected void configure(HttpSecurity http) throws Exception { |
|
|
|
|
|
|
|
|
|
//允许跨域
|
|
|
|
|
http.cors().and().anonymous().and() |
|
|
|
|
// CSRF禁用,因为不使用session
|
|
|
|
|
.csrf().disable() |
|
|
|
|
.formLogin().disable() |
|
|
|
|
.httpBasic().disable() |
|
|
|
|
.authorizeRequests() |
|
|
|
|
.accessDecisionManager(accessDecisionManager())//访问决策管理器
|
|
|
|
|
.antMatchers("/api/login").permitAll() |
|
|
|
|
.antMatchers("/system/init").permitAll() |
|
|
|
|
// 除上面外的所有请求全部需要鉴权认证
|
|
|
|
|
.anyRequest().authenticated() |
|
|
|
|
.and().exceptionHandling().accessDeniedHandler(accessDeniedHandler).and() |
|
|
|
|
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); |
|
|
|
|
|
|
|
|
|
http.addFilterAfter(new AuthCheckFilter(redisCacheTemplate), UsernamePasswordAuthenticationFilter.class) |
|
|
|
|
.addFilterAt(new LoginFilter(authenticationManager(), loginLogService, redisCacheTemplate), UsernamePasswordAuthenticationFilter.class) |
|
|
|
|
.exceptionHandling()//登录认证失败
|
|
|
|
|
.authenticationEntryPoint(((request, response, authException) -> { |
|
|
|
|
response.setCharacterEncoding("utf-8"); |
|
|
|
|
response.setContentType("application/json;charset=utf-8"); |
|
|
|
|
|
|
|
|
|
PrintWriter out = response.getWriter(); |
|
|
|
|
out.write(new ObjectMapper().writeValueAsString(ResultUtils.error(CodeMsg.USER_NEED_LOGIN))); |
|
|
|
|
out.flush(); |
|
|
|
|
out.close(); |
|
|
|
|
})); |
|
|
|
|
|
|
|
|
|
// 退出登录
|
|
|
|
|
http.logout() |
|
|
|
|
.logoutUrl("/logout") |
|
|
|
|
.deleteCookies("JSESSIONID") |
|
|
|
|
.logoutSuccessHandler(((request, response, authentication) -> { |
|
|
|
|
PrintWriter out = response.getWriter(); |
|
|
|
|
response.setContentType("application/json"); |
|
|
|
|
out.write(new ObjectMapper().writeValueAsString(ResultUtils.success("注销成功"))); |
|
|
|
|
out.flush(); |
|
|
|
|
out.close(); |
|
|
|
|
})); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// @Override
|
|
|
|
|
// protected void configure(HttpSecurity httpSecurity) throws Exception{
|
|
|
|
|
// //调试阶段
|
|
|
|
|
// httpSecurity.csrf().disable().authorizeRequests();
|
|
|
|
|
// httpSecurity.authorizeRequests().anyRequest()
|
|
|
|
|
// .permitAll().and().logout().permitAll();
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
//用于配置全局的某些通用事物,例如静态资源等
|
|
|
|
|
@Override |
|
|
|
|
public void configure(WebSecurity web) throws Exception { |
|
|
|
|
web.ignoring().antMatchers(WHITE_LIST); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// @Bean
|
|
|
|
|
// public PasswordEncoder delegatingPasswordEncoder(){
|
|
|
|
|
// return CustomDelegatingPasswordEncoder.createDelegatingPasswordEncoder();
|
|
|
|
|
// }
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|