1.前期准备工作

1.1首先需要导入jwt依赖和springsecurity的依赖

<!--security 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!--JWT 依赖 用来做登录验证--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency>

1.2 自定义application.yml配置jwt属性

# jwt令牌
jwt:# JWT存储的请求头# 正常前端请求 tokenHeader:Authorization 是key tokenHead:Bearer是它的 value 加上空格然后jwt令牌,组成一个请求tokenHeader: Authorization# JWT 加解密使用的密钥secret: yeb-secret# JWT的超期限时间(60*60*24)expiration: 604800# JWT 负载中拿到开头tokenHead: Bearer

1.3 编写jwt工具类

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** jwt token 工具类*/
@Component
@SuppressWarnings("all")
public class JwtTokenUtils {//用户名的keyprivate static final String CLAIM_KEY_USERNAME="sub";//jwt创建时间private static final String CLAIM_KEY_CREATED="created";@Value("${jwt.secret}")//jwt 密钥private String secret;@Value("${jwt.expiration}")//失效时间private Long expiration;/*** 根据用户信息生成token* @param userDetails* @return*///用户信息通过Security中的 UserDetails 拿取public String generateToken(UserDetails userDetails){Map<String,Object> jwtToken = new HashMap<>();jwtToken.put(CLAIM_KEY_USERNAME,userDetails.getUsername());jwtToken.put(CLAIM_KEY_CREATED,new Date());//根据荷载生成jwtreturn generateToken(jwtToken);}/*** 从token中获取登录用户名* @param token* @return*/public String getUserNameFromToken(String token){String username;try {Claims claims = getClaimsFromToken(token);//通过荷载 claims 就可以拿到用户名username = claims.getSubject();} catch (Exception e) {username = null;}return username;}/*** 判断token是否有效* @param token* @param userDetails* @return*/public boolean validateToken(String token, UserDetails userDetails) {String username = getUserNameFromToken(token);return username.equals(userDetails.getUsername()) && !isTokenExpired(token);}/*** 判断token是否可以被刷新* 如果过期了就可以被刷新,如果没过期就不能被刷新* @param token* @return*/public boolean canRefresh(String token){return !isTokenExpired(token);}/*** 刷新token* @param token* @return*/public String refreshToken(String token){Claims claims = getClaimsFromToken(token);//将创建时间改成当前时间,就相当于去刷新了claims.put(CLAIM_KEY_CREATED,new Date());return generateToken(claims);}/*** 判断token是否失效* @param token* @return*/private boolean isTokenExpired(String token) {Date expireDate = getExpiredDateFromToken(token);//判断token时间是否是当前时间的前面 .beforereturn expireDate.before(new Date());}/*** 从token中获取过期时间* @param token* @return*/private Date getExpiredDateFromToken(String token) {//从token里面获取荷载//因为token的过期时间有对应的数据,设置过的,荷载里面就有设置过的数据Claims claims = getClaimsFromToken(token);return claims.getExpiration();}/*** 从token中获取荷载* @param token* @return*/private Claims getClaimsFromToken(String token) {//拿到荷载Claims claims = null ;try {claims = Jwts.parser()//签名.setSigningKey(secret)//密钥.parseClaimsJws(token).getBody();} catch (ExpiredJwtException e) {e.printStackTrace();}return claims;}/*** 根据荷载生成 JWT TOKEN* @param claims* @return*/private String generateToken(Map<String,Object> claims){return Jwts.builder().setClaims(claims)//失效时间.setExpiration(generateExpirationDate())//签名.signWith(SignatureAlgorithm.HS512, secret).compact();}/*** 生成token失效时间* @return*/private Date generateExpirationDate() {return new Date(System.currentTimeMillis() + expiration * 1000);}}

1.4 登录流程

1.5 security登录流程

UsernamePasswordAuthenticationFilter:是我们最常用的用户名和密码认证方式的主要处理类,构造了一个UsernamePasswordAuthenticationToken对象实现类,将用请求信息封authentication

Authentication接口: 封装了用户相关信息

AuthenticationManage:定义了认证Authentication的方法,是认证相关的核心接口,也是发起认证的出发点,因为在实际需求中,我们可能会允许用户使用用户名+密码登录,同时允许用户使用邮箱+密码,手机号码+密码登录,甚至,可能允许用户使用指纹登录(还有这样的操作?没想到吧),所以说AuthenticationManager一般不直接认证,AuthenticationManager接口的常用实现类ProviderManager 内部会维护一个List列表,存放多种认证方式,实际上这是委托者模式的应用(Delegate)。也就是说,核心的认证入口始终只有一个:AuthenticationManager

AuthenticationManager,ProviderManager ,AuthenticationProvider…

用户名+密码(UsernamePasswordAuthenticationToken),邮箱+密码,手机号码+密码登录则对应了三个AuthenticationProvider

DaoAuthenticationProvider:用于解析并认证 UsernamePasswordAuthenticationToken 的这样一个认证服务提供者,对应以上的几种登录方式。

UserDetailsService接口:Spring Security 会将前端填写的username 传给 UserDetailService.loadByUserName方法。我们只需要从数据库中根据用户名查找到用户信息然后封装为UserDetails的实现类返回给SpringSecurity 即可,自己不需要进行密码的比对工作,密码比对交由SpringSecurity处理。

UserDetails接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装成UserDetails对象返回。然后将这些信息封装到Authentication对象中。

1.6 security基本原理

2.登录流程

继承UserDetails

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("f_user")
@ApiModel(value="FUser对象")
public class FUser implements Serializable, UserDetails{private static final long serialVersionUID = 1L;@TableId(type = IdType.ASSIGN_UUID)@ApiModelProperty(value = "用户唯一标识")private String id;@ApiModelProperty(value = "用户名")private String username;@ApiModelProperty(value = "手机号")private String phone;@ApiModelProperty(value = "密码")private String password;//自定义解析器@JsonDeserialize(using = CustomAuthorityDeserializer.class)@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<SimpleGrantedAuthority> authorities = roles.stream()//将获得的权限名字通过 SimpleGrantedAuthority 转换成授权的 url.map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList());return authorities;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}@TableField(exist = false)private List<FRole> roles;
}

UserController

@Api("用户接口")
@RestController
@RequestMapping("/user")
public class FUserController {@Autowiredprivate AccountUtil accountUtil;@Autowiredprivate IFUserService userService;@ApiOperation("是否注册")@RequestMapping(value ="/exist",method = RequestMethod.POST)public RespBean existUser(@RequestBody FUser fUser){String desEncrypt = accountUtil.desEncrypt(fUser.getPhone());FUser user = userService.getOne(new QueryWrapper<FUser>().eq("phone", desEncrypt));if(ObjectUtil.isEmpty(user)){return RespBean.success("可以注册,手机号还未注册");}return RespBean.error("手机号已注册");}@ApiOperation("注册")@RequestMapping(value ="/register",method = RequestMethod.PUT)public RespBean registerUser(@RequestBody FUser user){user.setPhone(accountUtil.desEncrypt(user.getPhone()));boolean save = userService.save(user);if(save){return RespBean.success("注册成功!");}return RespBean.error("注册失败");}@ApiOperation("登录之后返回token")@RequestMapping(value ="/login",method = RequestMethod.POST)public RespBean loginUser(@RequestBody LoginUser loginUser, HttpServletRequest request){return userService.loginUser(loginUser.getPhone(),loginUser.getPassword(),request);}@ApiOperation(value = "获取当前登录用户的信息")@GetMapping("/info")public FUser getUserInfo(Principal principal){if (principal == null){return null;}String username = principal.getName();FUser user = userService.getOneUser(username);user.setPassword(null);user.setRoles(userService.getRoles(user.getId()));return user;}}

服务实现类,这里我采用手机号登录,可以根据手机号查出这个对象不为空的话再去获得其

username再放给loadbyusername

@Service
public class FUserServiceImpl extends ServiceImpl<FUserMapper, FUser> implements IFUserService {@Autowiredprivate FRoleMapper roleMapper;@Autowiredprivate JwtTokenUtils jwtTokenUtils;@Autowiredprivate IFUserService userService;@Autowiredprivate UserDetailsService userDetailsService;//密码加密@Autowiredprivate PasswordEncoder bCryptPasswordEncoder;//将配置文件中存的值取过来@Value("${jwt.tokenHead}")private String tokenHead;@Overridepublic RespBean loginUser(String phone, String password, HttpServletRequest request) {//查询数据库中是否存在用户并判断当前登录用户密码与数据库密码是否匹配FUser user = userService.getUser(phone);if(ObjectUtil.isNull(user) || !bCryptPasswordEncoder.matches(password,user.getPassword())){return RespBean.error("用户名或密码不正确,请重新输入!");}UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());//更新security上下文登录用户对象,null的位置本来该放密码,但是一般不放//userDetails.getAuthorities()是权限列表UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());// 放在security全局里面SecurityContextHolder.getContext().setAuthentication(authentication);// 到这里说明没问题 就让他拿到令牌String token = jwtTokenUtils.generateToken(userDetails);Map<String, String> tokenMap = new HashMap<>();tokenMap.put("token", token);tokenMap.put("tokenHead", tokenHead);//返回头部信息// 登录成功之后返回一个token给前端return RespBean.success("登录成功", tokenMap);//tokenMap中有 username,new Date(),tokenHead}@Overridepublic List<FRole> getRoles(String id) {return roleMapper.getRoles(id);}@Overridepublic FUser getUser(String phone) {return userService.getOne(new QueryWrapper<FUser>().eq("phone",phone));}@Overridepublic FUser getOneUser(String username) {return userService.getOne(new QueryWrapper<FUser>().eq("username",username));}
}

jwt拦截器

/*** jwt登录授权过滤器*/
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Value("${jwt.tokenHeader}")private String tokenHeader;@Value("${jwt.tokenHead}")private String tokenHead;@Autowiredprivate JwtTokenUtils jwtTokenUtils;@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {//通过 request 获取请求头String authHeader = httpServletRequest.getHeader(tokenHeader);//验证头部,不存在,或者不是以tokenHead:Bearer开头的if (authHeader != null && authHeader.startsWith(tokenHead)){//存在,就做一个字符串的截取,其实就是获取了登录的tokenString authToken = authHeader.substring(tokenHead.length());//jwt根据token获取用户名//token存在用户名但是未登录String userName = jwtTokenUtils.getUserNameFromToken(authToken);if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null){//登录UserDetails userDetails = userDetailsService.loadUserByUsername(userName);//判断token是否有效,如果有效把他重新放到用户对象里面if (jwtTokenUtils.validateToken(authToken,userDetails)){UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));SecurityContextHolder.getContext().setAuthentication(authenticationToken);}}}//放行filterChain.doFilter(httpServletRequest,httpServletResponse);}}

securityconfig需要继承WebSecurityConfigureAdapter


/*** security配置类**/
//WebSecurityConfigurerAdapter 类是个适配器, 在配置的时候,需要我们自己写个配置类去继承他,然后编写自己所特殊需要的配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate IAdminService adminService;@Autowiredprivate RestAuthorizationEntryPoint restAuthorizationEntryPoint;@Autowiredprivate RestfulAccessDeniedHandler restfulAccessDeniedHandler;@Autowiredprivate CustomFilter customFilter;@Autowiredprivate CustomUrlDecisionManager customUrlDecisionManager;@Override//身份验证管理生成器protected void configure(AuthenticationManagerBuilder auth) throws Exception {//重写这个方法是因为,让登录的时候请求走自己重写的登录方法 UserDetailsService userDetailsService()//userDetailsService() 获取了用户名//asswordEncoder(passwordEncoder())密码匹配是通过BCryptPasswordEncoder加密来完成的auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
//        auth.userDetailsService(userDetailsService()).passwordEncoder(NoOpPasswordEncoder.getInstance());}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers(//放行的资源路径"/login","/logout","/css/**","/js/**","/index.html","favicon.ico","/doc.html","/captcha","/webjars/**","/swagger-resources/**","/v2/api-docs/**","/ws/**");}//security完整的配置@Overrideprotected void configure(HttpSecurity http) throws Exception {//使用jwt不需要csrfhttp.csrf().disable()//意思 使残废,关闭后面and之前的配置//基于token存储登录用户信息,不需要session,关闭session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()//所有请求都要求认证.anyRequest().authenticated()//动态权限,获取不同菜单列表.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {@Overridepublic <O extends FilterSecurityInterceptor> O postProcess(O o) {o.setAccessDecisionManager(customUrlDecisionManager);o.setSecurityMetadataSource(customFilter);return o;}}).and()//禁用缓存.headers().cacheControl();//添加 jwt 登录授权拦截器http.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);//添加自定义 未授权 和 未登录 结果返回http.exceptionHandling()//自定义 未授权.accessDeniedHandler(restfulAccessDeniedHandler)//自定义 未登录结果返回.authenticationEntryPoint(restAuthorizationEntryPoint);}@Override//重写获取用户的方法@Beanpublic UserDetailsService userDetailsService(){return username -> {Admin admin = adminService.getAdminByUserName(username);if (admin != null){admin.setRoles(adminService.getRoles(admin.getId()));return admin;}throw new UsernameNotFoundException("用户名或密码不正确!");};}@Beanpublic PasswordEncoder passwordEncoder(){//Security中默认的密码实现  BCryptPasswordEncoderreturn new BCryptPasswordEncoder();}//拦截器@Beanpublic JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){return new JwtAuthenticationTokenFilter();}}

权限控制过滤器

/*** 权限控制* 根据请求url分析请求所需的角色*/
@Component
public class CustomFilter implements FilterInvocationSecurityMetadataSource {@Autowiredprivate IFMenuService menuService;AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {//获取请求的urlString requestUrl = ((FilterInvocation) object).getRequestUrl();//根据角色去查询所有的菜单List<FMenu> menus = menuService.getMenuRoleList();//同时查询所有菜单的权限List<FMenu> menusAll = menuService.list();for (FMenu fMenu : menusAll) {if(fMenu.getUrl().equals(requestUrl)){for (FMenu menu : menus){//判断请求 url 与菜单角色是否匹配,如果这个url在菜单中存在但是没有匹配上就直接抛异常if (antPathMatcher.match(menu.getUrl(),requestUrl)){String[] strings = menu.getRoles().stream().map(FRole::getName).toArray(String[]::new);return SecurityConfig.createList(strings);}}throw new AccessDeniedException("权限不足,请联系管理员!");}}//没匹配的 url 默认登录就可以访问return SecurityConfig.createList("ROLE_LOGIN");}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {return null;}@Overridepublic boolean supports(Class<?> aClass) {return false;}
}

根据角色去匹配对应的url是否有权限


/*** @description: 判断用户角色* @author: Honors* @create: 2021-07-14 14:29*/
@Slf4j
@Component
public class CustomUrlDecisionManager implements AccessDecisionManager {@Overridepublic void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {for (ConfigAttribute configAttribute : configAttributes) {//当前 url 所需要的角色String needRole = configAttribute.getAttribute();if ("ROLE_LOGIN".equals(needRole)){if (authentication instanceof AnonymousAuthenticationToken){throw new AccessDeniedException("尚未登录,请登录!");}else {throw new AccessDeniedException("权限不足,请联系管理员!");}}//判断用户角色是否为url所需要角色Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();for (GrantedAuthority authority : authorities) {log.info("当前authority:"+authority.getAuthority());if (authority.getAuthority().equals(needRole)){return;}}}throw new AccessDeniedException("权限不足,请联系管理员!");}@Overridepublic boolean supports(ConfigAttribute configAttribute) {return false;}@Overridepublic boolean supports(Class<?> aClass) {return false;}
}

自定义序列化

/*** @description: 自定义authority字段解析器* @author: Honors*/
public class CustomAuthorityDeserializer extends JsonDeserializer {@Overridepublic Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {ObjectMapper mapper = (ObjectMapper) p.getCodec();JsonNode jsonNode = mapper.readTree(p);List<GrantedAuthority> grantedAuthorities = new LinkedList<>();Iterator<JsonNode> elements = jsonNode.elements();while (elements.hasNext()){JsonNode next = elements.next();//找到authority字段JsonNode authority = next.get("authority");//序列号,让json可以解析grantedAuthorities.add(new SimpleGrantedAuthority(authority.asText()));}return grantedAuthorities;}
}

Security自定义返回结果

/*** 当未登录或者token失效时访问接口是,自定义返回结果*/
@Component
public class RestAuthorizationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {response.setCharacterEncoding("UTF-8");//设置数据格式为json格式response.setContentType("application/json");//拿到输出流PrintWriter out = response.getWriter();//未登录或失效RespBean bean = RespBean.error("未登录或用户信息过期,请重新登录!");bean.setCode(401);out.write(new ObjectMapper().writeValueAsString(bean));out.flush();out.close();}
}
/*** 自定义授权* 当访问接口没有权限时,自定义返回结果*/
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {response.setCharacterEncoding("UTF-8");response.setContentType("application/json");PrintWriter out = response.getWriter();RespBean error = RespBean.error("权限不足,请联系管理员!");error.setCode(403);out.write(new ObjectMapper().writeValueAsString(error));out.flush();out.close();}
}

根据用户id查找角色,便于后面拿角色与根据url请求的角色做对比

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="wqm.store.mapper.FRoleMapper"><!-- 通用查询映射结果 --><resultMap id="BaseResultMap" type="wqm.store.pojo.FRole"><id column="id" property="id" /><result column="name" property="name" /></resultMap><!-- 通用查询结果列 --><sql id="Base_Column_List">id, name</sql><select id="getRoles" resultMap="getRoles" parameterType="String">select r.id,r.name from f_user_role ur,f_role r where ur.uid = #{id}and ur.rid = r.id</select><resultMap id="getRoles" type="FRole" extends="BaseResultMap"></resultMap></mapper>
@Repository
public interface FRoleMapper extends BaseMapper<FRole> {List<FRole> getRoles(@Param("id")String id);}
public interface IFUserService extends IService<FUser> {RespBean loginUser(String phone, String password, HttpServletRequest request);FUser getUser(String phone);FUser getOneUser(String username);List<FRole> getRoles(String id);
}

根据url查询所需角色

<select id="getMenus" resultMap="getMenus">select m.*,r.id as rid,r.name from f_role r, f_menu_role mr,f_menu m where r.id = mr.rid and mr.mid = m.id;</select><resultMap id="getMenus" type="FMenu" extends="BaseResultMap"><collection property="roles" ofType="FRole"><id property="id" column="rid"/><result property="name" column="name"/></collection></resultMap>
@Repository
public interface FMenuMapper extends BaseMapper<FMenu> {List<FMenu> getMenus();}
public interface IFMenuService extends IService<FMenu> {List<FMenu> getMenuRoleList();
}
@Service
public class FMenuServiceImpl extends ServiceImpl<FMenuMapper, FMenu> implements IFMenuService {@Autowiredprivate FMenuMapper menuMapper;@Overridepublic List<FMenu> getMenuRoleList() {return menuMapper.getMenus();}
}

Swagger配置,添加authrozation字段用来存放token

@Configuration
@EnableSwagger2
public class SwaggerConfig {@Bean//规定扫描哪些包下面生成swagger2文档public Docket createRestApi(){return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())//选择扫面哪个包.select().apis(RequestHandlerSelectors.basePackage("wqm.store.api.controller"))//所有的路径都可以.paths(PathSelectors.any()).build()//给swagger2令牌,不然测试接口太繁琐,需要登录会被拦截.securityContexts(securityContexts())//全局.securitySchemes(securitySchemes());//安全计划}private ApiInfo apiInfo(){return new ApiInfoBuilder()//标题.title("接口文档")//描述.description("").contact(new Contact("魏青冕","http://localhost:8080/doc.html","3139596057@qq.com")).version("1.0").build();}private List<ApiKey> securitySchemes(){//设置请求头信息List<ApiKey> result = new ArrayList<>();//令牌ApiKey apikey = new ApiKey("authorization","authorization","Header");result.add(apikey);return result;}private List<SecurityContext> securityContexts(){//设置需要登录认证的路径List<SecurityContext> result = new ArrayList<>();result.add(getContextBypath("/test3/.*"));return result;}private SecurityContext getContextBypath(String pathRegex) {return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.regex(pathRegex)).build();}private List<SecurityReference> defaultAuth() {List<SecurityReference> result = new ArrayList<>();AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];authorizationScopes[0] = authorizationScope;result.add(new SecurityReference("Authorization",authorizationScopes));return result;}}

测试如下

3.RBAC模型设计

/*Navicat Premium Data TransferSource Server         : 丫丫Source Server Type    : MySQLSource Server Version : 80028Source Host           : localhost:3306Source Schema         : fishstoreTarget Server Type    : MySQLTarget Server Version : 80028File Encoding         : 65001Date: 09/08/2022 13:28:21
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for f_menu
-- ----------------------------
DROP TABLE IF EXISTS `f_menu`;
CREATE TABLE `f_menu`  (`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`url` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of f_menu
-- ----------------------------
INSERT INTO `f_menu` VALUES ('67796ddf8305e03625ebdba3195ec312', '/test1');
INSERT INTO `f_menu` VALUES ('b443e9ad4898f4e5897274371562df76', '/test2');-- ----------------------------
-- Table structure for f_menu_role
-- ----------------------------
DROP TABLE IF EXISTS `f_menu_role`;
CREATE TABLE `f_menu_role`  (`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`rid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,`mid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of f_menu_role
-- ----------------------------
INSERT INTO `f_menu_role` VALUES ('db268eac1fc15596e175b46683066cb8', 'ab363ec9a5766ee42b15fc554d2e7c21', '67796ddf8305e03625ebdba3195ec312');-- ----------------------------
-- Table structure for f_role
-- ----------------------------
DROP TABLE IF EXISTS `f_role`;
CREATE TABLE `f_role`  (`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`name` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of f_role
-- ----------------------------
INSERT INTO `f_role` VALUES ('ab363ec9a5766ee42b15fc554d2e7c21', 'ROLE_stu');-- ----------------------------
-- Table structure for f_user
-- ----------------------------
DROP TABLE IF EXISTS `f_user`;
CREATE TABLE `f_user`  (`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户唯一标识',`username` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',`phone` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '手机号',`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '密码',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of f_user
-- ----------------------------
INSERT INTO `f_user` VALUES ('1ef48d092db370165b8e4cdfac9a0187', 'cjj', '13456789457', '$2a$10$ogvUqZZAxrBwrmVI/e7.SuFYyx8my8d.9zJ6bs9lPKWvbD9eefyCe');
INSERT INTO `f_user` VALUES ('2ff299d16d007aa37007394bae9bbcac', '会飞的鱼', '15295675946', '$2a$10$ogvUqZZAxrBwrmVI/e7.SuFYyx8my8d.9zJ6bs9lPKWvbD9eefyCe');-- ----------------------------
-- Table structure for f_user_role
-- ----------------------------
DROP TABLE IF EXISTS `f_user_role`;
CREATE TABLE `f_user_role`  (`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`uid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户主键',`rid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '角色主键',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of f_user_role
-- ----------------------------
INSERT INTO `f_user_role` VALUES ('695208cf31b61627c00bd987cfdd22e7', '2ff299d16d007aa37007394bae9bbcac', 'ab363ec9a5766ee42b15fc554d2e7c21');SET FOREIGN_KEY_CHECKS = 1;

定义参考:RBAC权限模型[完整] - 简书 (jianshu.com)

springsecurity整合jwt实现授权认证,权限分配相关推荐

  1. SpringSecurity - 整合JWT使用 Token 认证授权

    一.SpringSecurity 前面讲解了SpringSecurity的动态认证和动态权限角色,我们都知道在现在大多都是微服务前后端分离的模式开发,前面讲的还是基于Session的,本篇我们整合JW ...

  2. SpringSecurity权限管理系统实战—六、SpringSecurity整合JWT

    文章目录 系列目录 前言 一.无状态登录 二.JWT介绍 1.什么是jwt 头部(Header) 载荷(Payload) 签名(Signature) 2.JWT工作流程 3.简单实现 三.整合JWT ...

  3. Spring Boot整合JWT实现用户认证

    JWT实现用户认证 在介绍完JWT之后我们使用springboot整合JWT实现用户认证. 前后端分离使用JWT做用户认证(概述) JWT实现认证的原理 ​服务器在生成一个JWT之后会将这个JWT会以 ...

  4. 三分钟学会.NET Core Jwt 策略授权认证

    一.前言 大家好我又回来了,前几天讲过一个关于Jwt的身份验证最简单的案例,但是功能还是不够强大,不适用于真正的项目,是的,在真正面对复杂而又苛刻的客户中,我们会不知所措,就现在需要将认证授权这一块也 ...

  5. SpringSecurity 整合 JWT

    项目集成Spring Security(一) 在上一篇基础上继续集成 JWT ,实现用户身份验证. 前言 前后端分离项目中,如果直接把 API 接口对外开放,我们知道这样风险是很大的,所以在上一篇中我 ...

  6. jwt判断token是否过期_4spring-security5整合jwt做登录、权限验证,全网最全!!!可用...

    github源码: https://github.com/gyb123456/spring-security5-jwt,最烦那些写文档只截图一半还不给源码的人,要不你就截全图,要不就给源码! 前言: ...

  7. SpringBoot整合JWT实现登录认证

    目录 什么是JWT 为什么使用JWT JWT的使用场景 JWT的结构 JWT的请求流程 SpringBoot整合JWT 引入依赖 编写配置文件 token生成与验证工具类 拦截器拦截token 设置拦 ...

  8. springboot整合jwt_Spring Boot整合JWT实现用户认证(附源码)

    作者:LTLAYXhttps://dwz.cn/yv1Do6e3 推荐阅读 1. Java 性能优化:教你提高代码运行的效率 2. 基于token的多平台身份认证架构设计 3. select coun ...

  9. Spring Boot整合JWT实现用户认证(附源码)

    点击上方"程序IT圈",选择"置顶公众号" 每天早上8点50分进来看看,就是最大的支持 来源:https://dwz.cn/yv1Do6e3 什么是JWT JW ...

最新文章

  1. 好奇怪呀后面加什么标点_加标点
  2. SDUT 2384 El Dorado
  3. 【玩转server 2019系列】Windows server 2019打开windows defender提示“需要新应用打开windows defender”的解决方法
  4. python黑色背景编辑器_如何更换python默认编辑器的背景色
  5. C语言 输入一个正整数n,再输入n个字符,如果是小写字符就将其转换为大写字符,如果是大写字符就 转换为小写字符
  6. 使用计算机来解决实际问题首先要完成,《操作系统》试题库-填空题
  7. rdlc报表 矩形高固定_固定资产条码管理系统特点分析
  8. linux学习一个服务(未完)
  9. POJ 3122 分披萨(二分查找)
  10. 计算机网关,如何查看计算机的IP地址和网关
  11. 服务器行业深度解析:服务器未来需求知多少
  12. 24位掩码和30个掩码_高级ds位掩码和dp的问题
  13. 11 - JavaScript原型对象
  14. transforms常用函数简介
  15. 阿泰,水晶报表--pull模式样板
  16. 智能时代,谁是“大师”?
  17. MathType怎么编辑等号带点
  18. 彻底卸载Tomcat
  19. 如履薄冰:Redis 懒惰删除的巨大牺牲
  20. Yolanda,Withings,PICOOC以及RyFit四款智能人体成分秤对比评测

热门文章

  1. 常见的三种中文内码转换代码
  2. oracle job interval(关于时间误差)
  3. [LOJ#6617][THUPC2019]摆家具(矩阵乘法 + 子集和变换)
  4. ModuleNotFoundError: No module named ‘common‘
  5. open falcon mysql参数_open-falcon 监控MySQL及自定义监控指标
  6. JAVA视频学习笔记-马士兵(六)
  7. google浏览器(chrome)登录、同步
  8. 基于SSM的个人博客
  9. jieba库的安装与使用方法
  10. java 对 Map的遍历