committed by
Gitee
19 changed files with 408 additions and 251 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","/refreshToken", |
||||
|
"/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,48 @@ |
|||||
|
package com.bx.implatform.controller; |
||||
|
|
||||
|
|
||||
|
import com.bx.implatform.result.Result; |
||||
|
import com.bx.implatform.result.ResultUtils; |
||||
|
import com.bx.implatform.service.IUserService; |
||||
|
import com.bx.implatform.dto.LoginDTO; |
||||
|
import com.bx.implatform.dto.RegisterDTO; |
||||
|
import com.bx.implatform.vo.LoginVO; |
||||
|
import io.swagger.annotations.Api; |
||||
|
import io.swagger.annotations.ApiOperation; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
|
||||
|
import javax.validation.Valid; |
||||
|
|
||||
|
|
||||
|
|
||||
|
@Api(tags = "用户登录和注册") |
||||
|
@RestController |
||||
|
public class LoginController { |
||||
|
|
||||
|
@Autowired |
||||
|
private IUserService userService; |
||||
|
|
||||
|
@PostMapping("/login") |
||||
|
@ApiOperation(value = "用户注册",notes="用户注册") |
||||
|
public Result register(@Valid @RequestBody LoginDTO dto){ |
||||
|
LoginVO vo = userService.login(dto); |
||||
|
return ResultUtils.success(vo); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@PutMapping("/refreshToken") |
||||
|
@ApiOperation(value = "刷新token",notes="用refreshtoken换取新的token") |
||||
|
public Result refreshToken(@RequestHeader("refreshToken")String refreshToken){ |
||||
|
LoginVO vo = userService.refreshToken(refreshToken); |
||||
|
return ResultUtils.success(vo); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@PostMapping("/register") |
||||
|
@ApiOperation(value = "用户注册",notes="用户注册") |
||||
|
public Result register(@Valid @RequestBody RegisterDTO dto){ |
||||
|
userService.register(dto); |
||||
|
return ResultUtils.success(); |
||||
|
} |
||||
|
} |
||||
@ -1,32 +0,0 @@ |
|||||
package com.bx.implatform.controller; |
|
||||
|
|
||||
|
|
||||
import com.bx.implatform.result.Result; |
|
||||
import com.bx.implatform.result.ResultUtils; |
|
||||
import com.bx.implatform.service.IUserService; |
|
||||
import com.bx.implatform.vo.RegisterVO; |
|
||||
import io.swagger.annotations.Api; |
|
||||
import io.swagger.annotations.ApiOperation; |
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
|
||||
import org.springframework.web.bind.annotation.PostMapping; |
|
||||
import org.springframework.web.bind.annotation.RequestBody; |
|
||||
import org.springframework.web.bind.annotation.RestController; |
|
||||
|
|
||||
import javax.validation.Valid; |
|
||||
|
|
||||
|
|
||||
|
|
||||
@Api(tags = "用户注册") |
|
||||
@RestController |
|
||||
public class RegisterController { |
|
||||
|
|
||||
@Autowired |
|
||||
private IUserService userService; |
|
||||
|
|
||||
@PostMapping("/register") |
|
||||
@ApiOperation(value = "用户注册",notes="用户注册") |
|
||||
public Result register(@Valid @RequestBody RegisterVO dto){ |
|
||||
userService.register(dto); |
|
||||
return ResultUtils.success(); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.bx.implatform.dto; |
||||
|
|
||||
|
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 LoginDTO { |
||||
|
|
||||
|
//@NotEmpty(message="用户名不可为空")
|
||||
|
@ApiModelProperty(value = "用户名") |
||||
|
private String userName; |
||||
|
|
||||
|
// @NotEmpty(message="用户密码不可为空")
|
||||
|
@ApiModelProperty(value = "用户密码") |
||||
|
private String password; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
package com.bx.implatform.interceptor; |
||||
|
|
||||
|
|
||||
|
import com.alibaba.fastjson.JSON; |
||||
|
import com.auth0.jwt.exceptions.JWTVerificationException; |
||||
|
import com.bx.implatform.contant.Constant; |
||||
|
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 lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.web.method.HandlerMethod; |
||||
|
import org.springframework.web.servlet.HandlerInterceptor; |
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
; |
||||
|
|
||||
|
@Slf4j |
||||
|
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("accessToken"); |
||||
|
if (token == null) { |
||||
|
log.error("未登陆,url:{}",request.getRequestURI()); |
||||
|
throw new GlobalException(ResultCode.NO_LOGIN); |
||||
|
} |
||||
|
try{ |
||||
|
//验证 token
|
||||
|
JwtUtil.checkSign(token, Constant.ACCESS_TOKEN_SECRET); |
||||
|
}catch ( |
||||
|
JWTVerificationException e) { |
||||
|
log.error("token已失效,url:{}",request.getRequestURI()); |
||||
|
throw new GlobalException(ResultCode.INVALID_TOKEN); |
||||
|
} |
||||
|
// 存放session
|
||||
|
String strJson = JwtUtil.getInfo(token); |
||||
|
UserSession userSession = JSON.parseObject(strJson,UserSession.class); |
||||
|
request.setAttribute("session",userSession); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
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 java.util.Date; |
||||
|
|
||||
|
|
||||
|
public class JwtUtil { |
||||
|
|
||||
|
/** |
||||
|
* 生成jwt字符串,30分钟后过期 JWT(json web token) |
||||
|
* @param userId |
||||
|
* @param info |
||||
|
* @param expireIn |
||||
|
* @param secret |
||||
|
* @return |
||||
|
* */ |
||||
|
public static String sign(Long userId, String info,long expireIn,String secret) { |
||||
|
try { |
||||
|
Date date = new Date(System.currentTimeMillis() + expireIn*1000); |
||||
|
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 |
||||
|
* @param secret |
||||
|
* @return |
||||
|
* */ |
||||
|
public static boolean checkSign(String token,String secret) { |
||||
|
Algorithm algorithm = Algorithm.HMAC256(secret); |
||||
|
JWTVerifier verifier = JWT.require(algorithm) |
||||
|
//.withClaim("username, username)
|
||||
|
.build(); |
||||
|
verifier.verify(token); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.bx.implatform.vo; |
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
@ApiModel("用户登录VO") |
||||
|
public class LoginVO { |
||||
|
|
||||
|
@ApiModelProperty(value = "每次请求都必须在header中携带accessToken") |
||||
|
private String accessToken; |
||||
|
|
||||
|
@ApiModelProperty(value = "accessToken过期时间(秒)") |
||||
|
private Integer accessTokenExpiresIn; |
||||
|
|
||||
|
@ApiModelProperty(value = "accessToken过期后,通过refreshToken换取新的token") |
||||
|
private String refreshToken; |
||||
|
|
||||
|
@ApiModelProperty(value = "refreshToken过期时间(秒)") |
||||
|
private Integer refreshTokenExpiresIn; |
||||
|
} |
||||
Loading…
Reference in new issue