1. 前言
经历过一个项目后,基本可以把很多通用的配置保存起来,然后方便以后快速搭建一个完整可用的基础项目
整体是:SpringBoot+security+jwt(可选)+MybatisPlus
2. 配置
server:
port: 4037
#mybatis
mybatis-plus:
# mapper-locations: classpath*:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
spring:
profiles:
active: dev
---
#开发环境
spring:
profiles: dev
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/java_navigation?useSSL=false
artemis:
port: 4037
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
jwt:
secret: fas13fdfdsf
expiration: 86400000
header: Authorization
prefix: "Bearer "
cors:
origins:
- http://localhost:8080
headers:
- authentication
- Cache-Control
- X-User-Agent
- Content-Type
- Authorization
---
#生产环境
spring:
profiles: prod
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/java_navigation?useSSL=false&serverTimeZone=UTC
# url: jdbc:p6spy:mysql://localhost:3306/java_navigation?useSSL=false
logging:
file:
name: logs/spring.log
jwt:
secret: garfsdffdf!#fad
expiration: 86400000
header: Authorization
prefix: "Bearer "
cors:
origins:
- http://localhost:8080
headers:
- authentication
- Cache-Control
- X-User-Agent
- Content-Type
- Authorization
3. MybatisPlus
配置
以下包括分页插件的配置、自定义填充器
@Configuration
@MapperScan("com.ujuji.navigation.mapper")
public class MybatisPlusConfig {
@Bean
public MyMetaObjectHandler myMetaObjectHandler() {
return new MyMetaObjectHandler();
}
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
paginationInterceptor.setOverflow(true);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInterceptor.setLimit(100);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
Handler
主要就是填充处理createdAt和updatedAt两个字段
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("开始插入填充.....");
if (metaObject.hasSetter("createdAt")) {
log.info("createdAt");
this.strictInsertFill(metaObject, "createdAt", LocalDateTime.class, LocalDateTime.now());
}
if (metaObject.hasSetter("updatedAt")) {
log.info("updatedAt");
this.strictInsertFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now());
}
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("开始更新填充.....");
if (metaObject.hasSetter("updatedAt")) {
log.info("updatedAt");
this.strictUpdateFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now());
}
}
}
4. Security
配置
这里的配置内容,不算多,也不算少
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private JwtUtils jwtUtils;
@Resource
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new MyAccessDeniedHandler();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
//让Spring security放行所有preflight request,也即options预请求要放行
registry.requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/auth/**", "/").permitAll()
.antMatchers("/public/**", "/job/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtLoginFilter(authenticationManager(), jwtUtils),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtAuthenticationTokenFilter(userDetailsService, jwtUtils),
UsernamePasswordAuthenticationFilter.class)
.headers().cacheControl();
http.cors();
}
@Resource
CorsDataConfig corsDataConfig;
@Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
//指定允许跨域的请求(*所有):http://wap.ivt.guansichou.com
configuration.setAllowedOrigins(Collections.singletonList("*"));
// configuration.setAllowedOrigins(corsDataConfig.getOrigins());
configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
// setAllowCredentials(true) is important, otherwise:
// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
configuration.setAllowCredentials(true);
// setAllowedHeaders is important! Without it, OPTIONS preflight request
// will fail with 403 Invalid CORS request
configuration.setAllowedHeaders(corsDataConfig.getHeaders());
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
accessDeniedHandler
处理验证失败后怎么返回,因为这是前后端分离项目,所以这里是返回JSON
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
AppResult<Object> fail = AppResultBuilder.fail(ResultCode.PERMISSION_NO_ACCESS);
String string = new ObjectMapper().writeValueAsString(fail);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(string);//输出
writer.close();
}
}
JwtAuthenticationTokenFilter
这个主要是用来进行token认证处理的过滤器,简言之:获取header头中的token,然后判断是否有效、是否过期,如果验证成功且有效,那么构造一些信息,加入到security的全局上下文中(SecurityContextHolder)
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
/*通过构造参数注入*/
private final UserDetailsService userDetailsService;
private final JwtUtils jwtutils;
public JwtAuthenticationTokenFilter(UserDetailsService userDetailsService, JwtUtils jwtutils) {
this.userDetailsService = userDetailsService;
this.jwtutils = jwtutils;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(jwtutils.getHeader());
if (StringUtils.isNotEmpty(authHeader)) {
String[] strings = authHeader.split("\\s");
if (strings.length >= 2) {
authHeader = strings[1];
}
String username = jwtutils.getUsernameFromToken(authHeader);
log.info("加入凭证:{}", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// 判断如果username不为空,且上下文中没有数据,那么就尝试验证,
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
log.info("Details:{}", userDetails.toString());
if (jwtutils.validateToken(authHeader, userDetails)) {
// 且验证成功后,在上下文中加入凭证
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
}
JwtLoginFilter
这个过滤器就是用来登录的,继承自传统的UsernamePasswordAuthenticationFilter
,因为这是前后端分离项目,肯定不是传统的表单登录,所以需要重写登录处理的逻辑(attemptAuthentication
)
然后登录成功后的操作,登录失败后的操作
@Slf4j
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {
private final JwtUtils jwtUtils;
private final AuthenticationManager authenticationManager;
public JwtLoginFilter(AuthenticationManager authenticationManager, JwtUtils jwtUtils) {
this.authenticationManager = authenticationManager;
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/auth/login", "POST"));
this.jwtUtils = jwtUtils;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
log.info("Authentication-->>attemptAuthentication");
// 从输入流中获取到登录的信息
try {
UserEntity loginUser = new ObjectMapper().readValue(request.getInputStream(), UserEntity.class);
return this.authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword())
);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
// 成功验证后调用的方法
// 如果验证成功,就生成token并返回
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult) throws IOException, ServletException {
UserEntity user = (UserEntity) authResult.getPrincipal();
System.out.println("user:" + user.toString());
String role = "";
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
for (GrantedAuthority authority : authorities) {
role = authority.getAuthority();
}
String token = jwtUtils.generateToken(user);
//String token = JwtTokenUtils.createToken(user.getUsername(), false);
// 返回创建成功的token
// 但是这里创建的token只是单纯的token
// 按照jwt的规定,最后请求的时候应该是 `Bearer token`
// 获取用户信息
final SecurityContext context = SecurityContextHolder.getContext();
Map<String, Object> res = new HashMap<>();
user.setPassword(null);
res.put("userInfo", user);
res.put("token", token);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
AppResult<Object> appResult = AppResultBuilder.success(res, ResultCode.USER_LOGIN_SUCCESS);
String s = new ObjectMapper().writeValueAsString(appResult);
PrintWriter writer = response.getWriter();
writer.print(s);//输出
writer.close();
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
final String message = failed.getMessage();
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
AppResult<String> appResult;
if (failed instanceof InternalAuthenticationServiceException) {
appResult = AppResultBuilder.fail(ResultCode.USER_LOGIN_ERROR);
} else if (failed instanceof DisabledException) {
appResult = AppResultBuilder.fail(ResultCode.USER_ACCOUNT_FORBIDDEN);
} else if (StringUtils.isNoneBlank(message)) {
appResult = AppResultBuilder.fail(message);
} else {
appResult = AppResultBuilder.fail(ResultCode.USER_LOGIN_FAIL);
}
String s = new ObjectMapper().writeValueAsString(appResult);
PrintWriter writer = response.getWriter();
writer.print(s);//输出
writer.close();
}
}
一个获取信息的静态类
public class AuthInfo {
public static UserEntity getUserInfo() {
SecurityContext context = SecurityContextHolder.getContext();
return (UserEntity) context.getAuthentication().getPrincipal();
}
}
5. jwt
导入包:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
这个包已经很久没更新了,当然,也没更新的必要,算法就那样,也没啥漏洞之类的..
jwt工具类
@Data
@ConfigurationProperties(prefix = "jwt")
@Component
@Slf4j
public class JwtUtils implements Serializable {
private String secret;
private Long expiration;
private String header;
private String prefix;
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String generateToken(Map<String, Object> claims) {
Date expirationDate = new Date(System.currentTimeMillis() + expiration);
return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
/**
* 生成令牌
*
* @param userDetails 用户
* @return 令牌
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>(2);
claims.put("sub", userDetails.getUsername());
claims.put("created", new Date());
return generateToken(claims);
}
/**
* 从令牌中获取用户名
*
* @param token 令牌
* @return 用户名
*/
public String getUsernameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 判断令牌是否过期
*
* @param token 令牌
* @return 是否过期
*/
public Boolean isTokenExpired(String token) {
try {
Claims claims = getClaimsFromToken(token);
Date expiration = claims.getExpiration();
return expiration.before(new Date());
} catch (Exception e) {
return false;
}
}
/**
* 刷新令牌
*
* @param token 原令牌
* @return 新令牌
*/
public String refreshToken(String token) {
String refreshedToken;
try {
Claims claims = getClaimsFromToken(token);
claims.put("created", new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
/**
* 验证令牌
*
* @param token 令牌
* @param userDetails 用户
* @return 是否有效
*/
public Boolean validateToken(String token, UserDetails userDetails) {
UserEntity user = (UserEntity) userDetails;
String username = getUsernameFromToken(token);
return (username.equals(user.getUsername()) && !isTokenExpired(token));
}
}
6. 统一返回
ResultCode
//状态码
public enum ResultCode {
/* 成功状态码 */
SUCCESS(1000, "成功"),
/* 参数错误:10001-19999 */
PARAM_IS_INVALID(10001, "参数无效"),
PARAM_IS_BLANK(10002, "参数为空"),
PARAM_TYPE_BIND_ERROR(10003, "参数类型错误"),
PARAM_NOT_COMPLETE(10004, "参数缺失"),
/* 用户错误:20001-29999*/
USER_NOT_LOGGED_IN(20001, "用户未登录"),
USER_LOGIN_ERROR(20002, "账号不存在或密码错误"),
USER_ACCOUNT_FORBIDDEN(20003, "账号已被禁用"),
USER_NOT_EXIST(20004, "用户不存在"),
USER_HAS_EXISTED(20005, "用户已存在"),
USER_PASSWORD_ERROR(20006, "用户密码错误"),
USER_PASSWORD_MODIFY_SUCCESS(20007, "修改密码成功"),
USER_PASSWORD_MODIFY_FAIL(20008, "修改密码失败"),
USER_LOGIN_SUCCESS(20009, "登录成功"),
USER_LOGIN_FAIL(20010, "登录失败,请检查用户名和密码"),
USER_REGISTER_SUCCESS(20011, "注册成功"),
USER_REGISTER_FAIL(20012, "注册失败"),
USER_OLD_PASS_ERROR(20013, "旧密码错误"),
EMAIL_EXISTED(20014, "邮箱已存在"),
VERIFY_CODE_ERROR(20015, "验证码错误"),
/*业务:30001-39999*/
SERVICE_QUERY_FAIL(30001, "查询失败"),
SERVICE_INSERT_SUCCESS(30002, "添加成功"),
SERVICE_INSERT_FAIL(30003, "添加失败"),
SERVICE_DELETE_SUCCESS(30004, "删除成功"),
SERVICE_DELETE_FAIL(30005, "删除失败"),
SERVICE_UPDATE_SUCCESS(30006, "修改成功"),
SERVICE_UPDATE_FAIL(30007, "修改失败"),
USER_HAS_NOTICE(31001, "用户已存在一个公告,不能再次创建"),
USER_HAS_BOX(31002, "存在相同的一个盒子,不能创建"),
PLEASE_INPUT_USER_ID(31003, "抱歉,请输入用户ID"),
NOT_HAS_PERMISSION_OPERATOR(31004, "您没有操作此项操作的权限"),
PLEASE_INPUT_ID(31005, "请输入ID"),
BOX_NOT_EXISTED(31006, "盒子不存在,请检查"),
LINK_NOT_EXISTED(31007, "链接不存在,请检查"),
BOX_NOT_FOUND(31008, "未找到该导航"),
SELECT_ONE_FAIL(31009, "查询单条记录失败"),
SUFFIX_NOT_AVAILABLE(31010, "此后缀(标签)不可用,请更换"),
NAME_EXISTED(31011, "名称已存在"),
SUFFIX_NOT_EXISTED(31012, "此后缀不存在"),
BOX_PWD_ERROR(31013, "盒子密码错误"),
SUFFIX_LENGTH_ERROR(31014, "站点后缀长度小于规定"),
LEAVE_MSG_NOT_EXISTED(31015, "留言不存在"),
SET_MSG_FIXED_SUCCESS(31016, "留言置/取顶成功"),
SET_MSG_FIXED_FAIL(31017, "留言置/取顶失败"),
REPLY_MSG_SUCCESS(31018, "回复留言成功"),
REPLY_MSG_FAIL(31019, "回复留言失败"),
ITEM_NOT_EXISTS(31020, "该条目不存在"),
SET_MSG_READ_FAIL(31021, "设置留言已读失败"),
SET_MSG_READ_SUCCESS(31022, "设置留言已读成功"),
/* 权限错误:70001-79999 */
PERMISSION_NO_ACCESS(70001, "无访问权限"),
PERMISSION_NO_OPERATION(70002, "无操作权限"),
PERMISSION_NOT_MATCH_OPERATION(70003, "无操作权限");
private final Integer code;
private final String msg;
ResultCode(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
AppResult
public class AppResult<T> {
private int code;
private String msg;
private T data;// 数据
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
AppResultBuilder
public class AppResultBuilder {
//成功,不返回具体数据
public static <T> AppResult<T> successNoData(ResultCode code) {
AppResult<T> result = new AppResult<T>();
result.setCode(code.getCode());
result.setMsg(code.getMsg());
return result;
}
//成功,返回数据
public static <T> AppResult<T> success(T t, ResultCode code) {
AppResult<T> result = new AppResult<T>();
result.setCode(code.getCode());
result.setMsg(code.getMsg());
result.setData(t);
return result;
}
//失败,返回失败信息
public static <T> AppResult<T> fail(ResultCode code) {
AppResult<T> result = new AppResult<T>();
result.setCode(code.getCode());
result.setMsg(code.getMsg());
return result;
}
//失败,返回失败信息
public static <T> AppResult<T> fail(String msg, ResultCode code) {
AppResult<T> result = new AppResult<T>();
result.setCode(code.getCode());
result.setMsg(msg);
return result;
} //失败,返回失败信息
public static <T> AppResult<T> fail(String msg) {
AppResult<T> result = new AppResult<T>();
result.setMsg(msg);
return result;
}
}
7. 异常处理
自定义异常MyException
public class MyException extends RuntimeException {
private String message;
private ResultCode code;
public MyException() {
super();
}
public MyException(String message) {
super(message);
this.message = message;
}
public MyException(ResultCode code) {
super(code.getMsg());
this.message = code.getMsg();
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public ResultCode getCode() {
return code;
}
public void setCode(ResultCode code) {
this.code = code;
}
}
主要用来处理异常MyExceptionHandler
@RestControllerAdvice
@Slf4j
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value = MyException.class)
public AppResult<BaseEntity> entityHandler(MyException e) {
log.info("Exception Handler: {}", "entityHandler");
return AppResultBuilder.fail(e.getMessage(), e.getCode());
}
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public AppResult<Object> argumentsHandler(MethodArgumentNotValidException e) {
log.info("Exception Handler: {}", "argumentsHandler");
BindingResult bindingResult = e.getBindingResult();
FieldError fieldError = bindingResult.getFieldError();
assert fieldError != null;
return AppResultBuilder.fail(fieldError.getDefaultMessage());
}
@ExceptionHandler(value = Exception.class)
public AppResult<Object> globalHandler(Exception e) {
log.info("Exception Handler: {}", "globalHandler");
log.error("系统错误:{}", e.getMessage());
return AppResultBuilder.fail("系统错误,请联系管理员");
}
/**
* 这里处理没有权限的异常,不能在SecurityConfig里面处理,因为要被Exception捕获了
*
* @param e 异常
* @return 返回的信息
*/
@ExceptionHandler(value = AccessDeniedException.class)
public AppResult<Object> accessDeniedHandler(Exception e) {
log.info("Exception Handler: {}", "accessDeniedHandler");
log.error("accessDeniedHandler:{}", e.getMessage());
return AppResultBuilder.fail(ResultCode.PERMISSION_NO_ACCESS);
}
}
两点要说:
1、由于想通用,那么这里就捕获了Exception.class
最大的异常,导致的一个后果就是security的异常处理(如没权限)哪里不能自动处理了,那么就只能也在该文件中单独处理AccessDeniedException.class
2、这里还处理了MethodArgumentNotValidException.class
,这是实体类中的validation
的验证失败处理,也就是返回验证失败的内容
解决filter抛出的异常
在filter中捕获,使用下面两条代码:
request.setAttribute("filter.error", e);
request.getRequestDispatcher("/error/throw").forward(request, response);
新建ErrorController
@RestController
public class ErrorController {
/**
* 重新抛出异常
*/
@RequestMapping("/error/throw")
public void rethrow(HttpServletRequest request) {
throw ((MyException) request.getAttribute("filter.error"));
}
}
相当于就是转发到Controller层,在Controller层抛出异常,这样的话就可以使用全局捕获了!
8. 实体类
BaseEntity
@Data
public class BaseEntity {
@TableId(type = IdType.AUTO)
private Integer id;
@TableField(fill = FieldFill.INSERT)//INSERT代表只在插入时填充
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")//set
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")//get
protected LocalDateTime createdAt;
@TableField(fill = FieldFill.INSERT_UPDATE)// INSERT_UPDATE 首次插入、其次更新时填充(或修改)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")//set
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")//get
protected LocalDateTime updatedAt;
}
UserEntity
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@TableName("users")
public class UserEntity extends BaseEntity implements UserDetails {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
@Length(min = 6, message = "密码长度最少6位")
// @JsonIgnore
private String password;
@NotBlank(message = "邮箱不能为空")
@Pattern(regexp = "\\w+@\\w+(\\.\\w{2,3})*\\.\\w{2,3}", message = "邮箱格式不准确")
private String email;
private Integer enable;
@Null(message = "请勿传入非法参数")
private String authority;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> list = new ArrayList<>();
list.add(new SimpleGrantedAuthority(authority));
return list;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enable == 1;
}
}
9. 可能需要的坐标
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>