DefaultControllerAdvice.java
package com.stu.manage.demo.config; import com.stu.manage.demo.result.Result; import com.stu.manage.demo.result.ResultEnum; import com.stu.manage.demo.result.ResultUtil; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody;
MybatisPlusConfig.java
package com.stu.manage.demo.config;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
// 配置类
// 如果这里使用 @MapperScan 则 在启动类 SpingbootTestApplication@MapperScan可以不用写
//@MapperScan("com.lomonkey.mapper")
@Configuration
@EnableTransactionManagement // 开启注解事务管理,等同于xml配置文件中的
public class MybatisPlusConfig implements TransactionManagementConfigurer {
@Resource(name="txManager1")
private PlatformTransactionManager txManager1;
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
paginationInnerInterceptor.setOverflow(true);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInnerInterceptor.setMaxLimit(50L);
// 开启 count 的 join 优化,只针对部分 left join
//paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
// paginationInnerInterceptor.setDialect();
// 添加分页拦截器插件
interceptor.addInnerInterceptor(paginationInnerInterceptor);
// 添加乐观锁配置
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
// 创建事务管理器1 其中DataSource会自动注入
//在Spring容器中,我们手工注解@Bean 将被优先加载,框架不会重新实例化其他的 PlatformTransactionManager 实现类。
// 关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager
// 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。
// 如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。
@Bean(name = "txManager1")
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 创建事务管理器2
// 实现接口 TransactionManagementConfigurer 方法,其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理器
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager1;
}
}
MymetaObjectHandler
package com.stu.manage.demo.config;
import java.util.Date;
import org.apache.ibatis.reflection.metaObject;
import org.springframework.stereotype.Component;
import com.baomidou.mybatisplus.core.handlers.metaObjectHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component //一定不要忘记吧处理器加到IOC容器中
public class MymetaObjectHandler implements metaObjectHandler {
//插入时候的填充策略
@Override
public void insertFill(metaObject metaObject) {
//设置字段的值(String fieldName字段名,Object fieldVal要传递的值,metaObject metaObject)
this.setFieldValByName("createTime",new Date(),metaObject);
this.strictInsertFill(metaObject, "modifyTime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用)
}
//更新时间的填充策略
@Override
public void updateFill(metaObject metaObject) {
this.strictUpdateFill(metaObject, "modifyTime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用)
}
}
SecurityConfiguration.java
package com.stu.manage.demo.config;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpMethod;
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.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlbasedCorsConfigurationSource;
import com.stu.manage.demo.config.service.UserDetailsServiceImpl;
import com.stu.manage.demo.constants.SecurityConstants;
import com.stu.manage.demo.exception.JwtAccessDeniedHandler;
import com.stu.manage.demo.exception.JwtAuthenticationEntryPoint;
import com.stu.manage.demo.filter.JWTAuthenticationFilter;
import com.stu.manage.demo.filter.JwtAuthorizationFilter;
import static java.util.Collections.singletonList;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final StringRedisTemplate stringRedisTemplate;
@Autowired
private JwtAccessDeniedHandler jwtAccessDeniedHandler;
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
public SecurityConfiguration(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
protected UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 设置自定义的userDetailsService以及密码编码器
auth.userDetailsService(userDetailsService()).passwordEncoder(bCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors(withDefaults())
.csrf().disable() // 禁用 CSRF 禁用自带的跨域策略
.authorizeRequests()
// 指定的接口直接放行
.antMatchers(SecurityConstants.SWAGGER_WHITELIST).permitAll() // swagger
//.antMatchers(HttpMethod.GET, SecurityConstants.SYSTEM).permitAll()
.antMatchers(HttpMethod.POST, SecurityConstants.AUTH_LOGIN_URL).permitAll()
//.antMatchers(SecurityConstants.HAS_AUTH).hasAuthority("SystemUserUpdate")
//.antMatchers("student/insert
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(singletonList("*"));
// configuration.setAllowedOriginPatterns(singletonList("*"));
configuration.setAllowedHeaders(singletonList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "PUT", "OPTIONS"));
configuration.setExposedHeaders(singletonList(SecurityConstants.TOKEN_HEADER));
configuration.setAllowCredentials(false);
configuration.setMaxAge(3600L);
UrlbasedCorsConfigurationSource source = new UrlbasedCorsConfigurationSource();
source.registerCorsConfiguration("
public static final String ROLE_CLAIMS = "rol";
public static final long EXPIRATION = 60 * 60L;
public static final long EXPIRATION_REMEMBER = 60 * 60 * 24 * 7L;
public static final String JWT_SECRET_KEY = "C*F-JaNdRgUkXn2r5u8x/A?D(G+KbPeShVmYq3s6v9y$B&E)H@McQfTjWnZr4u7w";
// JWT token defaults
public static final String TOKEN_HEADER = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
public static final String TOKEN_TYPE = "JWT";
// Swagger WHITELIST
public static final String[] SWAGGER_WHITELIST = {
"/swagger-ui.html",
"/swagger-ui
@PostMapping("/login-up")
public Result login(@RequestBody LoginRequest loginRequest) {
String token = null;
try {
token = loginService.createToken(loginRequest);
} catch (Exception e) {
log.error(e.getMessage());
return ResultUtil.error(ResultEnum.VERIFY_FAIL.getCode(), ResultEnum.VERIFY_FAIL.getMsg());
}
LoginToken loginToken = new LoginToken();
loginToken.setName(loginRequest.getUsername());
loginToken.setToken(token);
return ResultUtil.success(loginToken);
// return new ResponseEntity<>(httpHeaders, HttpStatus.OK);
}
}
StudentController
package com.stu.manage.demo.controller;
import com.stu.manage.demo.result.Result;
import com.stu.manage.demo.result.ResultEnum;
import com.stu.manage.demo.result.ResultUtil;
import com.stu.manage.demo.service.StudentService;
import com.stu.manage.demo.entity.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/student")
@Slf4j
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/getAll")
// @PreAuthorize("hasAnyRole('ROLE_ADMIN')")
@PreAuthorize("hasAuthority('SystemContent')")
public Result getAllStudents(){
List res = studentService.getAllStudents();
if(res!=null) {
return ResultUtil.success(res);
}else {
return ResultUtil.error(ResultEnum.DATA_IS_NULL.getCode(),ResultEnum.DATA_IS_NULL.getMsg());
}
}
@GetMapping("/find/id/{id}")
public Result studentById(@PathVariable("id")Integer id){
List res=studentService.getStudentById(id);
if(!res.isEmpty()){
return ResultUtil.success(res);
}else {
return ResultUtil.error(ResultEnum.STUDENT_NOT_EXIST.getCode(),ResultEnum.STUDENT_NOT_EXIST.getMsg());
}
}
@PostMapping ("/insert")
public Result insert(@RequestBody @Valid Student student){
int res=studentService.insertStudent(student);
if(res==1){
return ResultUtil.success(res);
}else {
return ResultUtil.error(ResultEnum.UNKNOWN_ERROR.getCode(),ResultEnum.UNKNOWN_ERROR.getMsg());
}
}
@PostMapping("/update")
//@PreAuthorize("hasAnyRole('ROLE_ROOT')")
@PreAuthorize("hasAuthority('SystemUserUpdate')")
public Result update(@RequestBody @Valid Student student){
int res=studentService.updateStudent(student);
if(res>0){
return ResultUtil.success();
}else {
return ResultUtil.error(ResultEnum.UPDATE_FAIL.getCode(),ResultEnum.UPDATE_FAIL.getMsg());
}
}
}
LoginRequest
package com.stu.manage.demo.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginRequest {
private String username;
private String password;
private Boolean rememberMe;
}
entity
JwtUser
package com.stu.manage.demo.entity;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class JwtUser implements UserDetails {
private Long id;
private String username;
private String password;
private Boolean enabled;
private Collection extends GrantedAuthority> authorities;
public JwtUser(TbUser tbUser) {
this.id = tbUser.getId();
this.username = tbUser.getUserName();
this.password = tbUser.getPassWord();
this.enabled = tbUser.getEnabled() == null ? true : tbUser.getEnabled();;
}
public void setAuthorities(List list) {
this.authorities = list;
}
public JwtUser() {}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return authorities;
}
@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 true;
}
public Long getId() {
return id;
}
}
LoginToken
package com.stu.manage.demo.entity;
import lombok.Data;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@Data
@ToString
public class LoginToken {
@NotNull
private String name;
private String token;
}
Student
package com.stu.manage.demo.entity;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Builder;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
@Builder
public class Student {
@TableId(value = "student_id", type = IdType.AUTO)
private Integer studentId;
@TableField("student_name")
@NotBlank(message = "姓名不能为空")
private String studentName;
@TableField("id_card")
@NotBlank(message = "身份证不能为空")
private String idCard;
@NotNull
private String sex;
@TableField("grade_id")
@NotNull
private Integer gradeId;
@TableField("class_id")
@NotNull
private Integer classId;
@TableField(value = "createtime", fill = FieldFill.INSERT)
private Date createTime;
@TableField(value = "modifytime", fill = FieldFill.INSERT_UPDATE)
private Date modifyTime;
@Version //乐观锁Version注解
private Integer version;
}
TbAuditbase
package com.stu.manage.demo.entity;
import java.util.Date;
import javax.validation.constraints.NotNull;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
public class TbAuditbase {
@TableField(value = "created", fill = FieldFill.INSERT)
@NotNull
private Date createTime;
@TableField(value = "updated", fill = FieldFill.UPDATE)
@NotNull
private Date modifyTime;
}
TbPermission
package com.stu.manage.demo.entity;
import javax.validation.constraints.NotNull;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class TbPermission extends TbAuditbase {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("parent_id")
@NotNull
private Long parentId;
@NotNull
private String name;
@NotNull
private String enname;
@NotNull
private String url;
private String description;
}
TbRole
package com.stu.manage.demo.entity;
import java.util.List;
import javax.validation.constraints.NotNull;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@TableName(value = "tb_role", resultMap = "baseResultMap")
@AllArgsConstructor
@NoArgsConstructor
public class TbRole extends TbAuditbase {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("parent_id")
@NotNull
private Long parentId;
@NotNull
private String name;
@NotNull
private String enname;
private String description;
// 一对多
private List rolePermissions;
}
TbRolePermission
package com.stu.manage.demo.entity;
import javax.validation.constraints.NotNull;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class TbRolePermission {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("permission_id")
@NotNull
private Long permissionId;
@TableField("role_id")
@NotNull
private Long roleId;
}
TbUser
package com.stu.manage.demo.entity;
import java.util.List;
import javax.validation.constraints.NotNull;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@TableName(value = "tb_user", resultMap = "baseResultMap")
@AllArgsConstructor
@NoArgsConstructor
public class TbUser extends TbAuditbase {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("username")
@NotNull
private String userName;
@TableField("password")
@NotNull
private String passWord;
@NotNull
private String phone;
private String email;
private Boolean enabled;
private List userRoles;
}
TbUserRole
package com.stu.manage.demo.entity;
import javax.validation.constraints.NotNull;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class TbUserRole {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("user_id")
@NotNull
private Long userId;
@TableField("role_id")
@NotNull
private Long roleId;
}
exception
JwtAccessDeniedHandler
package com.stu.manage.demo.exception;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
accessDeniedException = new AccessDeniedException("无权限操作!!");
response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());
}
}
JwtAuthenticationEntryPoint
package com.stu.manage.demo.exception;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}
}
filter
JWTAuthenticationFilter
package com.stu.manage.demo.filter;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.stu.manage.demo.constants.SecurityConstants;
import com.stu.manage.demo.dto.LoginRequest;
import com.stu.manage.demo.entity.JwtUser;
import com.stu.manage.demo.utils.JwtTokenUtils;
// 认证过滤器
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final StringRedisTemplate stringRedisTemplate;
private ThreadLocal rememberMe = new ThreadLocal<>();
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager, StringRedisTemplate stringRedisTemplate) {
this.authenticationManager = authenticationManager;
this.stringRedisTemplate = stringRedisTemplate;
// 设置登录请求的 URL
super.setFilterProcessesUrl(SecurityConstants.AUTH_LOGIN_URL);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
ObjectMapper objectMapper = new ObjectMapper();
try {
// 从输入流中获取到登录的信息
LoginRequest loginRequest = objectMapper.readValue(request.getInputStream(), LoginRequest.class);
rememberMe.set(loginRequest.getRememberMe());
// 这部分和attemptAuthentication方法中的源码是一样的,
// 只不过由于这个方法源码的是把用户名和密码这些参数的名字是死的,所以我们重写了一下
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(), loginRequest.getPassword());
return authenticationManager.authenticate(authRequest);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authentication) {
JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
List roles = jwtUser.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
// 创建 Token
String token = JwtTokenUtils.createToken(jwtUser.getUsername(), jwtUser.getId().toString(), roles, rememberMe.get());
stringRedisTemplate.opsForValue().set(jwtUser.getId().toString(), token);
// Http Response Header 中返回 Token
response.setHeader(SecurityConstants.TOKEN_HEADER, token);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage());
}
}
JwtAuthorizationFilter
package com.stu.manage.demo.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import com.stu.manage.demo.constants.SecurityConstants;
import com.stu.manage.demo.utils.JwtTokenUtils;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private final StringRedisTemplate stringRedisTemplate;
public JwtAuthorizationFilter(AuthenticationManager authenticationManager, StringRedisTemplate stringRedisTemplate) {
super(authenticationManager);
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
String token = request.getHeader(SecurityConstants.TOKEN_HEADER);
if (token == null || !token.startsWith(SecurityConstants.TOKEN_PREFIX)) {
SecurityContextHolder.clearContext();
chain.doFilter(request, response);
return;
}
String tokenValue = token.replace(SecurityConstants.TOKEN_PREFIX, "");
UsernamePasswordAuthenticationToken authentication = null;
try {
String previousToken = stringRedisTemplate.opsForValue().get(JwtTokenUtils.getId(tokenValue));
if (!token.equals(previousToken)) {
SecurityContextHolder.clearContext();
chain.doFilter(request, response);
return;
}
authentication = JwtTokenUtils.getAuthentication(tokenValue);
} catch (JwtException e) {
logger.error("Invalid jwt : " + e.getMessage());
}
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
}
mapper
TbPermissionMapper
package com.stu.manage.demo.mapper; import java.util.List; import org.springframework.stereotype.Repository; import com.baomidou.mybatisplus.core.mapper.baseMapper; import com.stu.manage.demo.entity.TbPermission; import com.stu.manage.demo.entity.TbUser; @Repository public interface TbPermissionMapper extends baseMapper{ List findByUser(TbUser tbUser); }
TbPermissionMapper.xml
id, parent_id, `name`, enname, url, description, created, updated
TbUserMapper.xml
resultid, username, `password`, phone, email, created, updated
Result
package com.stu.manage.demo.result; import lombok.Data; @Data public class Result{ private Integer code; private String msg; private T data; public Result() { super(); } public Result(Integer code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } public Integer getCode() { return code; } public void setCode(Integer 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; } @Override public String toString() { return "Result{" + "code=" + code + ", msg='" + msg + ''' + ", data=" + data + '}'; } }
ResultEnum
package com.stu.manage.demo.result;
public enum ResultEnum {
//可自行定义,与前端交互
UNKNOWN_ERROR(-1,"未知错误"),
SUCCESS(200,"成功"),
STUDENT_NOT_EXIST(1,"学生不存在"),
STUDENT_IS_EXISTS(2,"学生已存在"),
DATA_IS_NULL(3,"数据为空"),
DELETE_FAIL(5,"删除失败"),
UPDATE_FAIL(6,"更新失败"),
VERIFY_FAIL(7,"验证失败")
;
private Integer code;
private String msg;
ResultEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
ResultUtil
package com.stu.manage.demo.result;
public class ResultUtil {
public static Result success(Object object){
Result result = new Result();
result.setCode(ResultEnum.SUCCESS.getCode());
result.setMsg(ResultEnum.SUCCESS.getMsg());
result.setData(object);
return result;
}
public static Result success(){
return success(null);
}
public static Result error(Integer code,String msg){
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
}
service
LoginServiceImpl
package com.stu.manage.demo.service.impl;
import java.util.List;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.stu.manage.demo.dto.LoginRequest;
import com.stu.manage.demo.entity.TbPermission;
import com.stu.manage.demo.entity.TbUser;
import com.stu.manage.demo.mapper.LoginMapper;
import com.stu.manage.demo.mapper.TbUserMapper;
import com.stu.manage.demo.service.ITbPermissionService;
import com.stu.manage.demo.service.ITbUserService;
import com.stu.manage.demo.service.LoginService;
import com.stu.manage.demo.utils.JwtTokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
public class LoginServiceImpl implements LoginService {
private static final String TAG = LoginServiceImpl.class.getName();
// private static final Logger logger = Logger.getLogger(LoginServiceImpl.class.getName());
@Autowired
private LoginMapper loginMapper;
@Autowired
private ITbUserService tbUserService;
@Autowired
private ITbPermissionService permissionService;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public String getAdmin(String name){
return loginMapper.getAdmin(name);
}
@Override
public String createToken(LoginRequest loginRequest) {
TbUserMapper mapper = tbUserService.getMapper();
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userName", loginRequest.getUsername());
TbUser tbUser = mapper.selectOne(queryWrapper);
if (!tbUserService.check(loginRequest.getPassword(), tbUser.getPassWord())) {
throw new BadCredentialsException("The username or password is not correct");
}
List permissions = permissionService.findByUser(tbUser).stream().map(TbPermission::getEnname).collect(Collectors.toList());
String token = JwtTokenUtils.createToken(tbUser.getUserName(), tbUser.getId().toString(), permissions, true);
log.info(TAG, "{} createToken is [}", tbUser.getUserName(), token);
//logger.info("{} createToken is [}", tbUser.getUserName(), token);
stringRedisTemplate.opsForValue().set(tbUser.getId().toString(), token);
return token;
}
}
utils
CurrentUserUtils
package com.stu.manage.demo.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import com.stu.manage.demo.entity.TbUser;
import com.stu.manage.demo.service.ITbUserService;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CurrentUserUtils {
private final ITbUserService tbUserService;
public TbUser getCurrentUser() {
return tbUserService.findByName(getCurrentUserName());
}
private String getCurrentUserName() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() != null) {
return (String) authentication.getPrincipal();
}
return null;
}
}
JwtTokenUtils
package com.stu.manage.demo.utils;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.crypto.SecretKey;
import javax.xml.bind.DatatypeConverter;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import com.stu.manage.demo.constants.SecurityConstants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
public class JwtTokenUtils {
private static final byte[] API_KEY_SECRET_BYTES = DatatypeConverter.parsebase64Binary(SecurityConstants.JWT_SECRET_KEY);
private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(API_KEY_SECRET_BYTES);
public static String createToken(String username, String id, List permissions, boolean isRememberMe) {
long expiration = isRememberMe ? SecurityConstants.EXPIRATION_REMEMBER : SecurityConstants.EXPIRATION;
final Date createdDate = new Date();
final Date expirationDate = new Date(createdDate.getTime() + expiration * 1000);
String tokenPrefix = Jwts.builder()
.setHeaderParam("type", SecurityConstants.TOKEN_TYPE)
.signWith(SECRET_KEY, SignatureAlgorithm.HS256) // 密钥
.claim(SecurityConstants.ROLE_CLAIMS, String.join(",", permissions)) // 载荷
.setId(id) // jwt唯一标识
.setIssuer("SnailClimb") //jwt的签发者
.setIssuedAt(createdDate) //jwt的签发标识
.setSubject(username) //sub(Subject):代表这个JWT的主体,是一个json格式的字符串,作为什么用户的唯一标志
.setExpiration(expirationDate) // 过期时间
.compact(); //压缩为xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx这样的jwt
return SecurityConstants.TOKEN_PREFIX + tokenPrefix; // 添加 token 前缀 "Bearer ";
}
public static String getId(String token) {
Claims claims = getClaims(token);
return claims.getId();
}
public static UsernamePasswordAuthenticationToken getAuthentication(String token) {
Claims claims = getClaims(token);
List authorities = getAuthorities(claims);
String userName = claims.getSubject();
return new UsernamePasswordAuthenticationToken(userName, token, authorities);
}
private static List getAuthorities(Claims claims) {
String role = (String) claims.get(SecurityConstants.ROLE_CLAIMS);
return Arrays.stream(role.split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
private static Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
}
validator
FullName
package com.stu.manage.demo.validator;
import java.lang.annotation.documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
@documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FulleNamevalidator.class)
public @interface FullName {
String message() default "姓名格式错误";
Class[] groups() default {};
Class[] payload() default {};
}
FulleNamevalidator
package com.stu.manage.demo.validator; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import lombok.extern.slf4j.Slf4j; @Slf4j public class FulleNamevalidator implements ConstraintValidator{ private static final String FULL_NAME_REG_EXP = "(?!.*\s$)((?=\S)(?![0-9]+$)[\u4E00-\u9FA5A-Za-z0-9. ' ]{2,15})"; @Override public boolean isValid(String fullNameStr, ConstraintValidatorContext context) { if (fullNameStr == null) { return true; } log.info("fullName is {}", fullNameStr); return fullNameStr.matches(FULL_NAME_REG_EXP); } }
application.properties
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase?useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=root #spring.datasource.initialSize=20 #spring.datasource.minIdle=50 #spring.datasource.maxActive=500 server.port=8888 #mybatis plus mybatis-plus.mapper-locations=classpath:mapper/*.xml mybatis-plus.type-aliases-package=com.stu.manage.demo.entity logging.level.com.stu.manage.demo.mapper = debug spring.redis.host=localhost spring.redis.port=6379 spring.redis.timeout=10000 spring.redis.database=0 spring.redis.jedis.pool.max-active=10 spring.redis.jedis.pool.max-wait=-1 spring.redis.jedis.pool.max-idle=8 spring.redis.jedis.pool.min-idle=0pom.xml
4.0.0 org.springframework.boot spring-boot-starter-parent2.4.0 com.stu-mange demo0.0.1-SNAPSHOT demo Demo project for Spring Boot 1.8 0.10.7 29.0-jre 4.13.1 org.springframework.boot spring-boot-starterorg.springframework.boot spring-boot-starter-validationorg.springframework.boot spring-boot-starter-securityorg.springframework.security spring-security-testtest org.springframework.boot spring-boot-starter-jdbcorg.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-web-servicesorg.springframework.boot spring-boot-starter-webfluxorg.springframework.boot spring-boot-devtoolsruntime true org.postgresql postgresqlruntime org.springframework.boot spring-boot-starter-testtest mysql mysql-connector-javaruntime io.projectreactor reactor-testtest com.microsoft.sqlserver mssql-jdbcruntime org.apache.httpcomponents httpclient4.5 org.apache.httpcomponents httpmime4.5 org.apache.httpcomponents httpcore4.4.1 com.alibaba fastjson1.2.17 com.baomidou mybatis-plus-boot-starter3.4.0 com.alibaba druid1.1.9 io.springfox springfox-swagger22.9.2 io.springfox springfox-swagger-ui2.9.2 org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-data-redisio.jsonwebtoken jjwt-api${jwt.version} io.jsonwebtoken jjwt-impl${jwt.version} runtime io.jsonwebtoken jjwt-jackson${jwt.version} runtime com.google.guava guava${guava.version} junit junit${junit.version} test org.springframework.boot spring-boot-maven-plugin
该记录引用项目:https://github.com/Snailclimb/spring-security-jwt-guide、http://t.zoukankan.com/snailclimb-p-11631890.html



