Spring boot 整合Spring Security Jwt
记录学习Spring boot 整合Spring Security Jwt
学习参考 – 慢慢的干货
https://shimo.im/docs/OnZDwoxFFL8bnP1c/read
首先创建Spring Boot项目以及导入依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.22</version></dependency><!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.1.tmp</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.3.1.tmp</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId><version>1.7</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--security--><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency><!--jwt--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><!--hutool工具--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.5.7</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency></dependencies>
application.yml配置
# 数据库相关配置
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/securitytext?serverTimezone=GMT&characterEncoding=utf-8username: rootpassword: 1233dbcp2:test-on-borrow: truevalidation-query: SELECT 1hikari:max-lifetime: 30000servlet:multipart:max-file-size: 1024MBmax-request-size: 1024MB# mybatis-plus相关配置
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# mapper-locations: classpath*:/mapper/**Mapper.xml# 服务端口号
server:port: 8030# jwt配置
xiaojiang:jwt:secret: f4e2e52034348f86b67cde581c0f9eb5expire: 604800header: Authorization
创建数据库
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户id',`username` varchar(64) DEFAULT NULL COMMENT '用户名',`password` varchar(64) DEFAULT NULL COMMENT '用户密码',`avatar` varchar(255) DEFAULT NULL COMMENT '用户头像',`email` varchar(64) DEFAULT NULL COMMENT '用户邮箱',`city` varchar(64) DEFAULT NULL COMMENT '城市',`created_time` datetime DEFAULT NULL COMMENT '用户创建时间',`updated_time` datetime DEFAULT NULL COMMENT '用户修改时间',`last_login_time` datetime DEFAULT NULL COMMENT '用户上次登录时间',`statu` int(5) NOT NULL COMMENT '用户状态 1可用|0不可用',PRIMARY KEY (`id`),UNIQUE KEY `UK_USERNAME` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '$2a$10$R7zegeWzOXPw871CmNuJ6upC0v8D373GuLuTw8jn6NET4BkPRZfgK', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', '123@qq.com', '广州', '2021-01-12 22:13:53', '2021-01-16 16:57:32', '2020-12-30 08:38:37', '1');INSERT INTO `sys_user` VALUES ('2', 'test', '$2a$10$0ilP4ZD1kLugYwLCs4pmb.ZT9cFqzOZTNaMiHxrBnVIQUGUwEvBIO', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', 'test@qq.com', null, '2021-01-30 08:20:22', '2021-01-30 08:55:57', null, '1');-- ----------------------------
-- Table structure for sys_role
-- ----------------------------DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色id',`name` varchar(64) NOT NULL COMMENT '角色名称',`code` varchar(64) NOT NULL COMMENT '角色',`created_time` datetime DEFAULT NULL COMMENT'角色创建时间',`updated_time` datetime DEFAULT NULL COMMENT '角色修改时间',`statu` int(5) NOT NULL COMMENT '角色状态 1可用|0不可用',PRIMARY KEY (`id`),UNIQUE KEY `name` (`name`) USING BTREE,UNIQUE KEY `code` (`code`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', '超级管理员', 'admin', '2021-01-16 13:29:03', '2021-01-17 15:50:45', '1');INSERT INTO `sys_role` VALUES ('2', '普通用户', 'normal', '2021-01-04 10:09:14', '2021-01-30 08:19:52', '1');-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`user_id` bigint(20) NOT NULL COMMENT '用户id',`role_id` bigint(20) NOT NULL COMMENT '角色id',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1', '1');
INSERT INTO `sys_user_role` VALUES ('2', '1', '2');
INSERT INTO `sys_user_role` VALUES ('3', '2', '2');
补上两张菜单表
-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (`id` int(20) NOT NULL AUTO_INCREMENT,`parent_id` int(20) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',`name` varchar(64) NOT NULL COMMENT '菜单名称',`path` varchar(255) DEFAULT NULL COMMENT '菜单URL',`perms` varchar(255) DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',`component` varchar(255) DEFAULT NULL,`type` int(5) NOT NULL COMMENT '类型 0:目录 1:菜单 2:按钮',`icon` varchar(32) DEFAULT NULL COMMENT '菜单图标',`created_time` datetime NOT NULL COMMENT '菜单创建时间',`updated_time` datetime DEFAULT NULL COMMENT '菜单修改时间',`statu` int(5) NOT NULL COMMENT '状态1可用|0不可用',PRIMARY KEY (`id`),UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of sys_menu
-- ----------------------------
INSERT INTO `sys_menu` VALUES ('1', '0', '系统管理', '', 'sys:manage', '', '0', 'el-icon-s-operation', '2021-01-15 18:58:18', '2021-01-15 18:58:20', '1');INSERT INTO `sys_menu` VALUES ('2', '1', '用户管理', '/sys/users', 'sys:user:list', 'sys/User', '1', 'el-icon-s-custom', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');INSERT INTO `sys_menu` VALUES ('3', '1', '角色管理', '/sys/roles', 'sys:role:list', 'sys/Role', '1', 'el-icon-rank', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');INSERT INTO `sys_menu` VALUES ('4', '1', '菜单管理', '/sys/menus', 'sys:menu:list', 'sys/Menu', '1', 'el-icon-menu', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');INSERT INTO `sys_menu` VALUES ('5', '0', '系统工具', '', 'sys:tools', null, '0', 'el-icon-s-tools', '2021-01-15 19:06:11', null, '1');INSERT INTO `sys_menu` VALUES ('6', '5', '数字字典', '/sys/dicts', 'sys:dict:list', 'sys/Dict', '1', 'el-icon-s-order', '2021-01-15 19:07:18', '2021-01-18 16:32:13', '1');INSERT INTO `sys_menu` VALUES ('7', '3', '添加角色', '', 'sys:role:save', '', '2', '', '2021-01-15 23:02:25', '2021-01-17 21:53:14', '0');INSERT INTO `sys_menu` VALUES ('8', '2', '添加用户', null, 'sys:user:save', null, '2', null, '2021-01-17 21:48:32', null, '1');-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu` (`id` int(20) NOT NULL AUTO_INCREMENT,`role_id` int(20) NOT NULL,`menu_id` int(20) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8mb4;-- ----------------------------
-- Records of sys_role_menu
-- ----------------------------
INSERT INTO `sys_role_menu` VALUES ('1', '1', '1');
INSERT INTO `sys_role_menu` VALUES ('2', '1', '2');
INSERT INTO `sys_role_menu` VALUES ('3', '1', '3');
INSERT INTO `sys_role_menu` VALUES ('4', '1', '4');
INSERT INTO `sys_role_menu` VALUES ('5', '1', '5');
INSERT INTO `sys_role_menu` VALUES ('6', '1', '6');
INSERT INTO `sys_role_menu` VALUES ('7', '1', '7');
INSERT INTO `sys_role_menu` VALUES ('8', '1', '8');
INSERT INTO `sys_role_menu` VALUES ('9', '2', '1');
INSERT INTO `sys_role_menu` VALUES ('10', '2', '2');
INSERT INTO `sys_role_menu` VALUES ('11', '2', '3');
INSERT INTO `sys_role_menu` VALUES ('12', '2', '4');
INSERT INTO `sys_role_menu` VALUES ('13', '2', '5');
使用mybatis-plus代码生成器
public class CodeGenerator {public static String scanner(String tip) {Scanner scanner = new Scanner(System.in);StringBuilder help = new StringBuilder();help.append("请输入" + tip + ":");System.out.println(help.toString());if (scanner.hasNext()) {String input= scanner.next();if (StringUtils.isNotEmpty(input)) {return input;}}throw new MybatisPlusException("请输入正确的" + tip + "!");}public static void main(String[] args) {// 代码生成器AutoGenerator mpg = new AutoGenerator();// 全局配置GlobalConfig gc = new GlobalConfig();String projectPath = System.getProperty("user.dir");gc.setOutputDir(projectPath + "/src/main/java");gc.setAuthor("Mr.jiang");gc.setOpen(false);gc.setServiceName("%sService");mpg.setGlobalConfig(gc);// 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setDbType(DbType.MYSQL);dsc.setUrl("jdbc:mysql://localhost:3306/securitytext?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai");dsc.setDriverName("com.mysql.cj.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("1233");mpg.setDataSource(dsc);// 包配置PackageConfig pc = new PackageConfig();pc.setModuleName(null);pc.setParent("com.jsp");mpg.setPackageInfo(pc);// 策略配置StrategyConfig strategy = new StrategyConfig();strategy.setNaming(NamingStrategy.underline_to_camel);strategy.setColumnNaming(NamingStrategy.underline_to_camel);strategy.setEntityLombokModel(true);strategy.setRestControllerStyle(true);strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));strategy.setControllerMappingHyphenStyle(true);strategy.setTablePrefix("sys_");mpg.setStrategy(strategy);mpg.execute();}
}
创建mybatis-plus配置类
@Configuration
public class MybatisPlusConfig {// 分页配置@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();return paginationInterceptor;}
}
@Component
public class MyMetaObjectConfig implements MetaObjectHandler {// 自动插入、更新@Overridepublic void insertFill(MetaObject metaObject) {this.setFieldValByName("createTime", new Date(), metaObject);this.setFieldValByName("updateTime", new Date(), metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {this.setFieldValByName("updateTime", new Date(), metaObject);}
}
这里是为了entiy类中的字段
/*** 用户创建时间*/@TableField(fill = FieldFill.INSERT)@JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone = "GMT+8")private LocalDateTime createdTime;/*** 用户修改时间*/@TableField(fill = FieldFill.INSERT)@JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone = "GMT+8")private LocalDateTime updatedTime;/*** 用户上次登录时间*/@TableField(fill = FieldFill.INSERT_UPDATE)@JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone = "GMT+8")private LocalDateTime lastLoginTime;
Result结果集
@Data
public class Result implements Serializable {private int code;private String message;private Object data;Result(int code,String message,Object data) {this.code = code;this.message = message;this.data = data;}Result(int code,String message) {this.code = code;this.message = message;}
}
public enum ResultCode {//成功SUCCESS(200),//失败FAIL(400),//用户未认证UNAUTHORIZED(401),//url找不到NOT_FOUND(404),//访问受限FORBIDDEN(403),//服务器错误INTERNAL_SERVER_ERROR(500);public int code;ResultCode(int code) {this.code = code;}
}
public class ResultFactory {//成功public static Result successful(Object data) {return buildResult(ResultCode.SUCCESS, "成功", data);}//失败public static Result fail(String message) {return buildResult(ResultCode.FAIL, message, null);}public static Result buildResult(int ResultCode, String message, Object data) {return new Result(ResultCode, message, data);}public static Result buildResult(ResultCode ResultCode, String message, Object data) {return new Result(ResultCode.code, message, data);}public static Result buildResult(ResultCode ResultCode, String message) {return new Result(ResultCode.code, message);}}
** 重点 Spring Security 配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate LoginFailureHandler loginFailureHandler;@Autowiredprivate LoginSuccessHandler loginSuccessHandler;@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Autowiredprivate JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;@Autowiredprivate JwtAccessDeniedHandler jwtAccessDeniedHandler;@Autowiredprivate JwtLogoutSuccessHandler jwtLogoutSuccessHandler;@BeanJWTAuthenticationFilter jwtAuthenticationFilter() throws Exception {return new JWTAuthenticationFilter(authenticationManager());}@BeanBCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}//白名单public static final String[] URL_WHITELIST = {"/favicon.ico","/login","/logout",};@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors().and().csrf().disable()//关闭session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().formLogin().successHandler(loginSuccessHandler).failureHandler(loginFailureHandler)//退出.and().logout().logoutSuccessHandler(jwtLogoutSuccessHandler)//资源访问控制.and().authorizeRequests()//白名单.antMatchers(URL_WHITELIST).permitAll()//其他请求需要认证.anyRequest().authenticated()//异常处理器.and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).accessDeniedHandler(jwtAccessDeniedHandler).and().addFilter(jwtAuthenticationFilter());}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService);}
}
重写UserDetails
public class MyUserDetails implements UserDetails {private Integer id;private String password;private final String username;private final Collection<? extends GrantedAuthority> authorities;private final boolean accountNonExpired;private final boolean accountNonLocked;private final boolean credentialsNonExpired;private final boolean enabled;public MyUserDetails(Integer id, String username, String password, Collection<? extends GrantedAuthority> authorities) {this(id, username, password, true, true, true, true, authorities);}public MyUserDetails(Integer id, String username, String password, boolean enabled, boolean accountNonExpired,boolean credentialsNonExpired, boolean accountNonLocked,Collection<? extends GrantedAuthority> authorities) {Assert.isTrue(username != null && !"".equals(username) && password != null,"Cannot pass null or empty values to constructor");this.id = id;this.username = username;this.password = password;this.enabled = enabled;this.accountNonExpired = accountNonExpired;this.credentialsNonExpired = credentialsNonExpired;this.accountNonLocked = accountNonLocked;this.authorities = authorities;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authorities;}@Overridepublic String getPassword() {return this.password;}@Overridepublic String getUsername() {return this.username;}@Overridepublic boolean isAccountNonExpired() {return this.accountNonExpired;}@Overridepublic boolean isAccountNonLocked() {return this.accountNonLocked;}@Overridepublic boolean isCredentialsNonExpired() {return this.credentialsNonExpired;}@Overridepublic boolean isEnabled() {return this.enabled;}
}
重写UserDetailsService
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate UserService userService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//查询数据库判断username是否存在,如果不存在则抛出异常UsernameNotFoundExceptionQueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("username", username);User user = userMapper.selectOne(wrapper);if (user == null) {throw new UsernameNotFoundException("用户名或密码不正确!");}//从数据库查出的用户信息id,username,password,authorityListList<GrantedAuthority> authorityList = getUserAuthority(user.getId());return new MyUserDetails(user.getId(), user.getUsername(), user.getPassword(), authorityList);}public List<GrantedAuthority> getUserAuthority(Integer id) {return AuthorityUtils.commaSeparatedStringToAuthorityList(userService.getUserAuthorityInfo(id));}
}
public interface UserRoleMapper extends BaseMapper<UserRole> {@Select("SELECT code, name FROM sys_user_role JOIN sys_role ON sys_user_role.role_id = sys_role.id WHERE user_id = #{uid};")List<Role> selectRoles(Integer uid);}
public interface UserMapper extends BaseMapper<User> {@Select("SELECT rm.menu_id FROM sys_user_role ur LEFT JOIN sys_role_menu rm ON ur.role_id = rm.role_id WHERE ur.user_id = #{uid};")List<Integer> getNavMenuIds(Integer uid);}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Autowiredprivate UserRoleMapper userRoleMapper;@Autowiredprivate UserMapper userMapper;//根据用户id获取用户角色@Overridepublic String getUserAuthorityInfo(Integer id) {List<Role> roles = userRoleMapper.selectRoles(id);List<Integer> menuIds = userMapper.getNavMenuIds(id);List<Menu> menus = menuMapper.selectBatchIds(menuIds);String roleNames = roles.stream().map(role -> "ROLE_" + role.getCode()).collect(Collectors.joining(","));String permNames = menus.stream().map(menu -> menu.getPerms()).collect(Collectors.joining(","));// ROLE_admin,ROLE_normal,sys:user:list...,String authority = roleNames.concat(",").concat(permNames);return authority;}@Overridepublic User getByUsername(String username) {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("username", username);User user = userMapper.selectOne(wrapper);return user;}
}
LoginSuccessHandler
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {@Autowiredprivate JwtUtil jwtUtil;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = response.getOutputStream();// 生成jwt返回String token = jwtUtil.generateToken(authentication.getName());response.setHeader(jwtUtil.getHeader(), token);Result result = ResultFactory.successful("");outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));outputStream.flush();outputStream.close();}
}
LoginFailureHandler
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {response.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = response.getOutputStream();Result result = ResultFactory.fail("Bad credentials".equals(exception.getMessage()) ? "用户名或密码不正确" : exception.getMessage());outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));outputStream.flush();outputStream.close();}
}
JwtLogoutSuccessHandler
@Component
public class JwtLogoutSuccessHandler implements LogoutSuccessHandler {@Autowiredprivate JwtUtil jwtUtil;@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {if (authentication != null) {new SecurityContextLogoutHandler().logout(request, response, authentication);}response.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = response.getOutputStream();response.setHeader(jwtUtil.getHeader(), "");Result result = ResultFactory.successful("");outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));outputStream.flush();outputStream.close();}
}
JwtAuthenticationEntryPoint
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {response.setContentType("application/json;charset=UTF-8");response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);ServletOutputStream outputStream = response.getOutputStream();Result result = ResultFactory.buildResult(ResultCode.UNAUTHORIZED, "请先登录!");outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));outputStream.flush();outputStream.close();}
}
JwtAccessDeniedHandler
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {response.setContentType("application/json;charset=UTF-8");response.setStatus(HttpServletResponse.SC_FORBIDDEN);ServletOutputStream outputStream = response.getOutputStream();//Result result = ResultFactory.fail(e.getMessage());Result result = ResultFactory.buildResult(ResultCode.FORBIDDEN,"权限不足");outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));outputStream.flush();outputStream.close();}
}
jwt相关配置
JwtUtil
@Data
@Component
@ConfigurationProperties(prefix = "xiaojiang.jwt")
public class JwtUtil {private long expire;private String secret;private String header;//生成JWTpublic String generateToken(String username) {Date exireDate = new Date(new Date().getTime() + 1000 * expire);return Jwts.builder().setHeaderParam("typ", "JWT").setSubject(username).setIssuedAt(new Date()).setExpiration(exireDate).signWith(SignatureAlgorithm.HS256, secret).compact();}//解析JWTpublic Claims getClaimByToken(String jwt) {try {return Jwts.parser().setSigningKey(secret).parseClaimsJws(jwt).getBody();} catch (Exception e) {return null;}}//JWT是否过期public boolean isTokenExpired(Claims claims) {return claims.getExpiration().before(new Date());}
}
JWTAuthenticationFilter
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {@Autowiredprivate UserService userService;@Autowiredprivate JwtUtil jwtUtil;@Autowiredprivate UserDetailsServiceImpl userDetailsService;public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {super(authenticationManager);}protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {String jwt = request.getHeader(jwtUtil.getHeader());if (StrUtil.isBlankOrUndefined(jwt)) {chain.doFilter(request, response);return;}Claims claim = jwtUtil.getClaimByToken(jwt);if (claim == null) {throw new JwtException("token异常");}if (jwtUtil.isTokenExpired(claim)) {throw new JwtException("token已过期");}String username = claim.getSubject();//获取用户的权限等信息User user = userService.getByUsername(username);List<GrantedAuthority> userAuthority = userDetailsService.getUserAuthority(user.getId());UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, userAuthority);System.out.println("token->" + token);SecurityContextHolder.getContext().setAuthentication(token);chain.doFilter(request, response);}}
ok大功告成 使用postman试试效果
admin用户拥有admin和normal角色,test用户拥有normal角色
在Controller中分配接口权限
// 如果使用 @PreAuthorize("hasRole('ROLE_ADMIN')")需要在类的头部加上@EnableGlobalMethodSecurity(prePostEnabled = true) //打开权限验证@RestController
@RequestMapping("/user")
@EnableGlobalMethodSecurity(prePostEnabled = true) //打开权限验证
public class UserController {@Autowiredprivate UserService userService;@PreAuthorize("hasRole('admin')")@GetMapping("/admin")public String hello() {return "hello admin";}@PreAuthorize("hasRole('normal')")@GetMapping("/test")public String test() {return "hello test";}@PreAuthorize("hasRole('admin')")@GetMapping("/list")public Result list() {return ResultFactory.successful(userService.list());}}
首先使用admin用户登录
在Header中有Authorization,也就是我们需要的token
接着使用admin用户的token去访问其他接口
admin用户都可以访问
接着使用test用户登录
使用test用户去访问其他接口
因为test用户只有normal角色,所以只能访问test接口,admin接口权限不足
还待完善!!!
Spring boot 整合Spring Security Jwt相关推荐
- spring boot整合spring security笔记
最近自己做了一个小项目,正在进行springboot和spring Security的整合,有一丢丢的感悟,在这里分享一下: 首先,spring boot整合spring security最好是使用T ...
- 认证与授权流程与spring boot整合 spring security(1)
一 spring security 1.1 spring security的作用 Spring Security所解决的问题就是安全访问控制,而安全访问控制功能其实就是对所有进入系统的请求进行拦截 ...
- springboot整合hibernate_峰哥说技术系列-17 .Spring Boot 整合 Spring Data JPA
今日份主题 Spring Boot 整合 Spring Data JPA JPA(Java Persistence API)是用于对象持久化的 API,是Java EE 5.0 平台标准的 ORM 规 ...
- Spring Boot 整合——Spring batch重试和回滚
关于版本 依赖 版本 springboot 2.4.0 spring batch 2.4.0 代码地址 因为每个例子涉及代码较多,且包含测试用例,如果都贴到文章中内容过多,所以只贴出了部分代码.全部的 ...
- 二、何为Spring Boot整合Spring Cloud?
题语:学习方法之多思考:正向.逆向.跳跃 作者:A哥(YourBatman) wx号:fsx641385712(备注"Java群"字样) 公众号:BAT的乌托邦(ID:BAT-ut ...
- spring boot 整合 spring cache 简单使用
spring boot 整合 spring cache 简单使用 spring cache简介 使用spring cache spring cache简介 Spring 3.1起,提供了基于注解的对C ...
- Spring Boot 整合——Spring Boot整合kafka整合
Spring Boot 整合之前的内容 项目名称 描述 地址 base-data-mybatis 整合mybatis-plus(实际上官方教程已经很多,只做了自定义插件) 未完成 base-jpa J ...
- Spring Boot 整合 Spring Security 示例
点击关注公众号,Java干货及时送达 一.说明 SpringSecurity是一个用于Java 企业级应用程序的安全框架,主要包含用户认证和用户授权两个方面.相比较Shiro而言,Security功能 ...
- Spring Boot整合Spring Data JPA操作数据
一. Sping Data JPA 简介 Spring Data JPA 是 Spring 基于 ORM 框架.JPA 规范的基础上封装的一套 JPA 应用框架,底层使用了 Hibernate 的 J ...
最新文章
- C++知识点53——虚继承
- 优化 UI 应用启动时间的方法
- 服务器 风扇测试软件,图解服务器风扇安装的正确方法
- asterisk1.8 Makefile分析 (1)
- html之属性的应用
- 【OpenCV 例程200篇】51. 图像增强—直方图反向追踪
- Python 创建随机mac地址(单播、组播)
- kubernetes英语怎么读_小学三年级英语怎么学
- 刘乾四川大学计算机学院刘乾,计算机学院2011~2012学年-四川大学计算机学院.doc...
- 电脑快捷方式变白原因及解决方法——血的教训呜呜呜
- exagear安装java_exagear模拟器怎么使用 exagear模拟器使用方法安装教程
- debian设置IP
- 金蝶云星空html5的网页主界面如何修改,金蝶云星空启用科目管控后,科目相关的值更新事件无法生效原因及解决办法...
- 献计《权力的游戏》珊莎•斯塔克
- python快速幂算法解决大数取模
- Scratch编程初体验-小猫跳舞
- 【安卓wechat微信导出聊天记录】
- excel汇总怎么做?
- 关于MAX232芯片总是发热的问题
- vm15安装mac 未能与服务器联系,VMware15/16解锁VMware安装MacOS的步骤详解
热门文章
- node-sass是什么?
- 首届“发现杯”软件设计大奖赛启动
- (阿里云)Linux部署SSM项目全过程
- 高性能服务器设计[转自腾讯km,由qzhang同学翻译]
- stm32心率监测系统(心率监测,wifi上传,APP显示,上位机显示)
- 解决谷歌的but your computer or network may be sending automated queries
- nuc企业微信自动打卡 autojs
- 日文配列键盘修改和映射自定义
- Latex中一些特殊常用符号的输入
- 谷粒商城基础篇——Day01