15 changed files with 269 additions and 207 deletions
@ -0,0 +1,36 @@ |
|||||
|
package com.bx.implatform.config; |
||||
|
|
||||
|
import com.bx.implatform.interceptor.AuthInterceptor; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
||||
|
import org.springframework.security.crypto.password.PasswordEncoder; |
||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||
|
|
||||
|
|
||||
|
@Configuration |
||||
|
public class MvcConfig implements WebMvcConfigurer { |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public void addInterceptors(InterceptorRegistry registry) { |
||||
|
registry.addInterceptor(authInterceptor()) |
||||
|
.addPathPatterns("/**") |
||||
|
.excludePathPatterns( "/image/upload","/login","/logout","/register", |
||||
|
"/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Bean |
||||
|
public AuthInterceptor authInterceptor() { |
||||
|
return new AuthInterceptor(); |
||||
|
} |
||||
|
|
||||
|
@Bean |
||||
|
public PasswordEncoder passwordEncoder(){ |
||||
|
// 使用BCrypt加密密码
|
||||
|
return new BCryptPasswordEncoder(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -1,172 +0,0 @@ |
|||||
package com.bx.implatform.config; |
|
||||
|
|
||||
import com.alibaba.fastjson.JSON; |
|
||||
import com.bx.implatform.enums.ResultCode; |
|
||||
import com.bx.implatform.result.Result; |
|
||||
import com.bx.implatform.result.ResultUtils; |
|
||||
import com.bx.implatform.service.IUserService; |
|
||||
import com.bx.implatform.session.UserSession; |
|
||||
import com.fasterxml.jackson.databind.ObjectMapper; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
|
||||
import org.springframework.beans.factory.annotation.Qualifier; |
|
||||
import org.springframework.context.annotation.Bean; |
|
||||
import org.springframework.context.annotation.Configuration; |
|
||||
import org.springframework.security.authentication.*; |
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; |
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; |
|
||||
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.core.userdetails.User; |
|
||||
import org.springframework.security.core.userdetails.UserDetailsService; |
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
|
||||
import org.springframework.security.crypto.password.PasswordEncoder; |
|
||||
import org.springframework.security.web.AuthenticationEntryPoint; |
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler; |
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; |
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; |
|
||||
|
|
||||
import java.io.PrintWriter; |
|
||||
|
|
||||
/* |
|
||||
* SpringSecurity安全框架配置 |
|
||||
* |
|
||||
* @Author Blue |
|
||||
* @Date 2022/10/21 |
|
||||
*/ |
|
||||
@Slf4j |
|
||||
@Configuration |
|
||||
@EnableWebSecurity |
|
||||
@EnableGlobalMethodSecurity(prePostEnabled=true) |
|
||||
public class WebSecurityConfg extends WebSecurityConfigurerAdapter { |
|
||||
|
|
||||
|
|
||||
|
|
||||
@Qualifier("securityUserDetailsServiceImpl") |
|
||||
@Autowired |
|
||||
private UserDetailsService userDetailsService; |
|
||||
|
|
||||
@Autowired |
|
||||
private IUserService userService; |
|
||||
|
|
||||
@Override |
|
||||
protected void configure(HttpSecurity http) throws Exception { |
|
||||
http.authorizeRequests() |
|
||||
.antMatchers("/image/upload","/login","/logout","/register","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**") |
|
||||
.permitAll() |
|
||||
.anyRequest() //任何其它请求
|
|
||||
.authenticated() //都需要身份认证
|
|
||||
.and() |
|
||||
// 登录配置表单认证方式
|
|
||||
.formLogin() |
|
||||
.usernameParameter("username")//设置登录账号参数,与表单参数一致
|
|
||||
.passwordParameter("password")//设置登录密码参数,与表单参数一致
|
|
||||
.loginProcessingUrl("/login")//配置默认登录入口
|
|
||||
.successHandler(successHandler()) |
|
||||
.failureHandler(failureHandler()) |
|
||||
.and() |
|
||||
// 注销
|
|
||||
.logout() |
|
||||
.logoutUrl("/logout") |
|
||||
.logoutSuccessHandler(logoutHandler()) |
|
||||
.permitAll() |
|
||||
.and() |
|
||||
// session管理
|
|
||||
.sessionManagement() |
|
||||
.and() |
|
||||
// 禁用跨站csrf攻击防御
|
|
||||
.csrf() |
|
||||
.disable() |
|
||||
.exceptionHandling() |
|
||||
.authenticationEntryPoint(entryPoint()); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
@Bean |
|
||||
AuthenticationFailureHandler failureHandler(){ |
|
||||
return (request, response, exception) -> { |
|
||||
response.setContentType("application/json;charset=utf-8"); |
|
||||
PrintWriter out = response.getWriter(); |
|
||||
Result result = ResultUtils.error(ResultCode.LOGIN_ERROR,exception.getMessage()); |
|
||||
if (exception instanceof LockedException) { |
|
||||
result =ResultUtils.error(ResultCode.LOGIN_ERROR,"账户被锁定,请联系管理员!"); |
|
||||
} else if (exception instanceof CredentialsExpiredException) { |
|
||||
result = ResultUtils.error(ResultCode.LOGIN_ERROR,"密码过期,请联系管理员!"); |
|
||||
} else if (exception instanceof AccountExpiredException) { |
|
||||
result =ResultUtils.error(ResultCode.LOGIN_ERROR,"账户过期,请联系管理员!"); |
|
||||
} else if (exception instanceof DisabledException) { |
|
||||
result = ResultUtils.error(ResultCode.LOGIN_ERROR,"账户被禁用,请联系管理员!"); |
|
||||
} else if (exception instanceof BadCredentialsException) { |
|
||||
result =ResultUtils.error(ResultCode.LOGIN_ERROR,"用户名或者密码输入错误,请重新输入!"); |
|
||||
} |
|
||||
out.write(new ObjectMapper().writeValueAsString(result)); |
|
||||
out.flush(); |
|
||||
out.close(); |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
@Bean |
|
||||
AuthenticationSuccessHandler successHandler(){ |
|
||||
return (request, response, authentication) -> { |
|
||||
User useDetail = (User)authentication.getPrincipal(); |
|
||||
String strJson = useDetail.getUsername(); |
|
||||
UserSession userSession = JSON.parseObject(strJson,UserSession.class); |
|
||||
log.info("用户 '{}' 登录,id:{},昵称:{}",userSession.getUserName(),userSession.getId(),userSession.getNickName()); |
|
||||
// 响应
|
|
||||
response.setContentType("application/json;charset=utf-8"); |
|
||||
PrintWriter out = response.getWriter(); |
|
||||
Result result = ResultUtils.success(); |
|
||||
out.write(new ObjectMapper().writeValueAsString(result)); |
|
||||
out.flush(); |
|
||||
out.close(); |
|
||||
|
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@Bean |
|
||||
LogoutSuccessHandler logoutHandler(){ |
|
||||
return (request, response, authentication) -> { |
|
||||
User useDetail = (User)authentication.getPrincipal(); |
|
||||
String strJson = useDetail.getUsername(); |
|
||||
UserSession userSession = JSON.parseObject(strJson,UserSession.class); |
|
||||
log.info("用户 '{}' 退出,id:{},昵称:{}",userSession.getUserName(),userSession.getId(),userSession.getNickName()); |
|
||||
// 响应
|
|
||||
response.setContentType("application/json;charset=utf-8"); |
|
||||
PrintWriter out = response.getWriter(); |
|
||||
Result result = ResultUtils.success(); |
|
||||
out.write(new ObjectMapper().writeValueAsString(result)); |
|
||||
out.flush(); |
|
||||
out.close(); |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
@Bean |
|
||||
AuthenticationEntryPoint entryPoint(){ |
|
||||
return (request, response, exception) -> { |
|
||||
response.setContentType("application/json;charset=utf-8"); |
|
||||
PrintWriter out = response.getWriter(); |
|
||||
Result result = ResultUtils.error(ResultCode.NO_LOGIN); |
|
||||
out.write(new ObjectMapper().writeValueAsString(result)); |
|
||||
out.flush(); |
|
||||
out.close(); |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
|
||||
@Bean |
|
||||
public PasswordEncoder passwordEncoder(){ |
|
||||
// 使用BCrypt加密密码
|
|
||||
return new BCryptPasswordEncoder(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@Override |
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception { |
|
||||
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
||||
@ -0,0 +1,37 @@ |
|||||
|
package com.bx.implatform.interceptor; |
||||
|
|
||||
|
|
||||
|
import com.alibaba.fastjson.JSON; |
||||
|
import com.bx.implatform.enums.ResultCode; |
||||
|
import com.bx.implatform.exception.GlobalException; |
||||
|
import com.bx.implatform.session.UserSession; |
||||
|
import com.bx.implatform.util.JwtUtil; |
||||
|
import org.springframework.web.method.HandlerMethod; |
||||
|
import org.springframework.web.servlet.HandlerInterceptor; |
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
; |
||||
|
|
||||
|
public class AuthInterceptor implements HandlerInterceptor { |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
||||
|
//如果不是映射到方法直接通过
|
||||
|
if (!(handler instanceof HandlerMethod)) { |
||||
|
return true; |
||||
|
} |
||||
|
//从 http 请求头中取出 token
|
||||
|
String token = request.getHeader("token"); |
||||
|
if (token == null) { |
||||
|
throw new GlobalException(ResultCode.NO_LOGIN); |
||||
|
} |
||||
|
//验证 token
|
||||
|
JwtUtil.checkSign(token); |
||||
|
// 存放session
|
||||
|
String strJson = JwtUtil.getInfo(token); |
||||
|
UserSession userSession = JSON.parseObject(strJson,UserSession.class); |
||||
|
request.setAttribute("session",userSession); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,94 @@ |
|||||
|
package com.bx.implatform.util; |
||||
|
|
||||
|
import com.auth0.jwt.JWT; |
||||
|
import com.auth0.jwt.JWTVerifier; |
||||
|
import com.auth0.jwt.algorithms.Algorithm; |
||||
|
import com.auth0.jwt.exceptions.JWTDecodeException; |
||||
|
import com.auth0.jwt.exceptions.JWTVerificationException; |
||||
|
import com.bx.implatform.exception.GlobalException; |
||||
|
; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
public class JwtUtil { |
||||
|
/** |
||||
|
* 过期5分钟 |
||||
|
* */ |
||||
|
private static final long EXPIRE_TIME = 30 * 60 * 1000; |
||||
|
|
||||
|
/** |
||||
|
* jwt密钥 |
||||
|
* */ |
||||
|
private static final String SECRET = "MIIBIjANBgkq"; |
||||
|
|
||||
|
/** |
||||
|
* 生成jwt字符串,30分钟后过期 JWT(json web token) |
||||
|
* @param userId |
||||
|
* @param info |
||||
|
* @return |
||||
|
* */ |
||||
|
public static String sign(Long userId, String info) { |
||||
|
try { |
||||
|
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); |
||||
|
Algorithm algorithm = Algorithm.HMAC256(SECRET); |
||||
|
return JWT.create() |
||||
|
//将userId保存到token里面
|
||||
|
.withAudience(userId.toString()) |
||||
|
//存放自定义数据
|
||||
|
.withClaim("info", info) |
||||
|
//五分钟后token过期
|
||||
|
.withExpiresAt(date) |
||||
|
//token的密钥
|
||||
|
.sign(algorithm); |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据token获取userId |
||||
|
* @param token |
||||
|
* @return |
||||
|
* */ |
||||
|
public static Long getUserId(String token) { |
||||
|
try { |
||||
|
String userId = JWT.decode(token).getAudience().get(0); |
||||
|
return Long.parseLong(userId); |
||||
|
}catch (JWTDecodeException e) { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据token获取自定义数据info |
||||
|
* @param token |
||||
|
* @return |
||||
|
* */ |
||||
|
public static String getInfo(String token) { |
||||
|
try { |
||||
|
return JWT.decode(token).getClaim("info").asString(); |
||||
|
}catch (JWTDecodeException e) { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 校验token |
||||
|
* @param token |
||||
|
* @return |
||||
|
* */ |
||||
|
public static boolean checkSign(String token) { |
||||
|
try { |
||||
|
Algorithm algorithm = Algorithm.HMAC256(SECRET); |
||||
|
JWTVerifier verifier = JWT.require(algorithm) |
||||
|
//.withClaim("username, username)
|
||||
|
.build(); |
||||
|
verifier.verify(token); |
||||
|
return true; |
||||
|
}catch (JWTVerificationException e) { |
||||
|
throw new GlobalException("token 无效,请重新获取"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.bx.implatform.vo; |
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
import org.hibernate.validator.constraints.Length; |
||||
|
|
||||
|
import javax.validation.constraints.NotEmpty; |
||||
|
|
||||
|
@Data |
||||
|
@ApiModel("用户登录VO") |
||||
|
public class LoginVO { |
||||
|
|
||||
|
//@NotEmpty(message="用户名不可为空")
|
||||
|
@ApiModelProperty(value = "用户名") |
||||
|
private String userName; |
||||
|
|
||||
|
// @NotEmpty(message="用户密码不可为空")
|
||||
|
@ApiModelProperty(value = "用户密码") |
||||
|
private String password; |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue