百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证

toyiye 2024-06-21 19:09 12 浏览 0 评论

20大进阶架构专题每日送达

作者:Sans_

juejin.im/post/5da82f066fb9a04e2a73daec

一.说明

SpringSecurity是一个用于Java 企业级应用程序的安全框架,主要包含用户认证和用户授权两个方面.相比较Shiro而言,Security功能更加的强大,它可以很容易地扩展以满足更多安全控制方面的需求,但也相对它的学习成本会更高,两种框架各有利弊.实际开发中还是要根据业务和项目的需求来决定使用哪一种.

JWT是在Web应用中安全传递信息的规范,从本质上来说是Token的演变,是一种生成加密用户身份信息的Token,特别适用于分布式单点登陆的场景,无需在服务端保存用户的认证信息,而是直接对Token进行校验获取用户信息,使单点登录更为简单灵活.

二.项目环境

  • SpringBoot版本:2.1.6

  • SpringSecurity版本: 5.1.5

  • MyBatis-Plus版本: 3.1.0

  • JDK版本:1.8

  • 数据表(SQL文件在项目中):数据库中测试号的密码进行了加密,密码皆为123456

Maven依赖如下:

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<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.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- MybatisPlus 核心库 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<!-- 引入阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<!-- StringUtilS工具 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!-- JSON工具 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
<!-- JWT依赖 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
</dependencies>

配置如下:

# 配置端口
server:
port: 8764
spring:
# 配置数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sans_security?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertTo&useSSL=false
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
# JWT配置
jwt:
# 密匙KEY
secret: JWTSecret
# HeaderKEY
tokenHeader: Authorization
# Token前缀字符
tokenPrefix: Sans-
# 过期时间 单位秒 1天后过期=86400 7天后过期=604800
expiration: 86400
# 配置不需要认证的接口
antMatchers: /index,/login/**,/favicon.ico
# Mybatis-plus相关配置
mybatis-plus:
# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath:mapper/*.xml
# 以下配置均有默认值,可以不设置
global-config:
db-config:
#主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: AUTO
#字段策略 IGNORED:"忽略判断" NOT_:"非 判断") NOT_EMPTY:"非空判断"
field-strategy: NOT_EMPTY
#数据库类型
db-type: MYSQL
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 返回map时true:当查询数据为空时字段返回为,false:不加这个查询数据为空时,字段将被隐藏
call-setters-on-s: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

三.编写项目基础类

Entity,Dao,Service,及等SpringSecurity用户的Entity,Service类等在这里省略,请参考源码

编写JWT工具类

/**
* JWT工具类
* @Author Sans
* @CreateTime 2019/10/2 7:42
*/
@Slf4j
public class JWTTokenUtil {

/**
* 生成Token
* @Author Sans
* @CreateTime 2019/10/2 12:16
* @Param selfUserEntity 用户安全实体
* @Return Token
*/
public static String createAccessToken(SelfUserEntity selfUserEntity){
// 登陆成功生成JWT
String token = Jwts.builder
// 放入用户名和用户ID
.setId(selfUserEntity.getUserId+"")
// 主题
.setSubject(selfUserEntity.getUsername)
// 签发时间
.setIssuedAt(new Date)
// 签发者
.setIssuer("sans")
// 自定义属性 放入用户拥有权限
.claim("authorities", JSON.toJSONString(selfUserEntity.getAuthorities))
// 失效时间
.setExpiration(new Date(System.currentTimeMillis + JWTConfig.expiration))
// 签名算法和密钥
.signWith(SignatureAlgorithm.HS512, JWTConfig.secret)
.compact;
return token;
}
}

编写暂无权限处理类

/**
* @Description 暂无权限处理类
* @Author Sans
* @CreateTime 2019/10/3 8:39
*/
@Component
public class UserAuthAccessDeniedHandler implements AccessDeniedHandler{
/**
* 暂无权限返回结果
* @Author Sans
* @CreateTime 2019/10/3 8:41
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception){
ResultUtil.responseJson(response,ResultUtil.resultCode(403,"未授权"));
}
}

编写用户未登录处理类

/**
* 用户未登录处理类
* @Author Sans
* @CreateTime 2019/10/3 8:55
*/
@Component
public class UserAuthenticationEntryPointHandler implements AuthenticationEntryPoint {
/**
* 用户未登录返回结果
* @Author Sans
* @CreateTime 2019/10/3 9:01
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
ResultUtil.responseJson(response,ResultUtil.resultCode(401,"未登录"));
}
}

编写登录失败处理类

/**
* @Description 登录失败处理类
* @Author Sans
* @CreateTime 2019/10/3 9:06
*/
@Slf4j
@Component
public class UserLoginFailureHandler implements AuthenticationFailureHandler {
/**
* 登录失败返回结果
* @Author Sans
* @CreateTime 2019/10/3 9:12
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
// 这些对于操作的处理类可以根据不同异常进行不同处理
if (exception instanceof UsernameNotFoundException){
log.info("【登录失败】"+exception.getMessage);
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用户名不存在"));
}
if (exception instanceof LockedException){
log.info("【登录失败】"+exception.getMessage);
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用户被冻结"));
}
if (exception instanceof BadCredentialsException){
log.info("【登录失败】"+exception.getMessage);
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用户名密码不正确"));
}
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"登录失败"));
}
}

编写登录成功处理类

/**
* @Description 登录成功处理类
* @Author Sans
* @CreateTime 2019/10/3 9:13
*/
@Slf4j
@Component
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler {
/**
* 登录成功返回结果
* @Author Sans
* @CreateTime 2019/10/3 9:27
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
// 组装JWT
SelfUserEntity selfUserEntity = (SelfUserEntity) authentication.getPrincipal;
String token = JWTTokenUtil.createAccessToken(selfUserEntity);
token = JWTConfig.tokenPrefix + token;
// 封装返回参数
Map<String,Object> resultData = new HashMap<>;
resultData.put("code","200");
resultData.put("msg", "登录成功");
resultData.put("token",token);
ResultUtil.responseJson(response,resultData);
}
}

编写登出成功处理类

/**
* 用户登出类
* @Author Sans
* @CreateTime 2019/10/3 9:42
*/
@Component
public class UserLogoutSuccessHandler implements LogoutSuccessHandler {
/**
* 用户登出返回结果
* 这里应该让前端清除掉Token
* @Author Sans
* @CreateTime 2019/10/3 9:50
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
Map<String,Object> resultData = new HashMap<>;
resultData.put("code","200");
resultData.put("msg", "登出成功");
SecurityContextHolder.clearContext;
ResultUtil.responseJson(response,ResultUtil.resultSuccess(resultData));
}
}

四.编写Security核心类

编写自定义登录验证类

/**
* 自定义登录验证
* @Author Sans
* @CreateTime 2019/10/1 19:11
*/
@Component
public class UserAuthenticationProvider implements AuthenticationProvider {
@Autowired
private SelfUserDetailsService selfUserDetailsService;
@Autowired
private SysUserService sysUserService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 获取表单输入中返回的用户名
String userName = (String) authentication.getPrincipal;
// 获取表单中输入的密码
String password = (String) authentication.getCredentials;
// 查询用户是否存在
SelfUserEntity userInfo = selfUserDetailsService.loadUserByUsername(userName);
if (userInfo == ) {
throw new UsernameNotFoundException("用户名不存在");
}
// 我们还要判断密码是否正确,这里我们的密码使用BCryptPasswordEncoder进行加密的
if (!new BCryptPasswordEncoder.matches(password, userInfo.getPassword)) {
throw new BadCredentialsException("密码不正确");
}
// 还可以加一些其他信息的判断,比如用户账号已停用等判断
if (userInfo.getStatus.equals("PROHIBIT")){
throw new LockedException("该用户已被冻结");
}
// 角色集合
Set<GrantedAuthority> authorities = new HashSet<>;
// 查询用户角色
List<SysRoleEntity> sysRoleEntityList = sysUserService.selectSysRoleByUserId(userInfo.getUserId);
for (SysRoleEntity sysRoleEntity: sysRoleEntityList){
authorities.add(new SimpleGrantedAuthority("ROLE_" + sysRoleEntity.getRoleName));
}
userInfo.setAuthorities(authorities);
// 进行登录
return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}

编写自定义PermissionEvaluator注解验证

/**
* 自定义权限注解验证
* @Author Sans
* @CreateTime 2019/10/6 13:31
*/
@Component
public class UserPermissionEvaluator implements PermissionEvaluator {
@Autowired
private SysUserService sysUserService;
/**
* hasPermission鉴权方法
* 这里仅仅判断PreAuthorize注解中的权限表达式
* 实际中可以根据业务需求设计数据库通过targetUrl和permission做更复杂鉴权
* @Author Sans
* @CreateTime 2019/10/6 18:25
* @Param authentication 用户身份
* @Param targetUrl 请求路径
* @Param permission 请求路径权限
* @Return boolean 是否通过
*/
@Override
public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
// 获取用户信息
SelfUserEntity selfUserEntity =(SelfUserEntity) authentication.getPrincipal;
// 查询用户权限(这里可以将权限放入缓存中提升效率)
Set<String> permissions = new HashSet<>;
List<SysMenuEntity> sysMenuEntityList = sysUserService.selectSysMenuByUserId(selfUserEntity.getUserId);
for (SysMenuEntity sysMenuEntity:sysMenuEntityList) {
permissions.add(sysMenuEntity.getPermission);
}
// 权限对比
if (permissions.contains(permission.toString)){
return true;
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return false;
}
}

编写SpringSecurity核心配置类

/**
* SpringSecurity核心配置类
* @Author Sans
* @CreateTime 2019/10/1 9:40
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启权限注解,默认是关闭的
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自定义登录成功处理器
*/
@Autowired
private UserLoginSuccessHandler userLoginSuccessHandler;
/**
* 自定义登录失败处理器
*/
@Autowired
private UserLoginFailureHandler userLoginFailureHandler;
/**
* 自定义注销成功处理器
*/
@Autowired
private UserLogoutSuccessHandler userLogoutSuccessHandler;
/**
* 自定义暂无权限处理器
*/
@Autowired
private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;
/**
* 自定义未登录的处理器
*/
@Autowired
private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;
/**
* 自定义登录逻辑验证器
*/
@Autowired
private UserAuthenticationProvider userAuthenticationProvider;

/**
* 加密方式
* @Author Sans
* @CreateTime 2019/10/1 14:00
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder{
return new BCryptPasswordEncoder;
}
/**
* 注入自定义PermissionEvaluator
*/
@Bean
public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler{
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler;
handler.setPermissionEvaluator(new UserPermissionEvaluator);
return handler;
}

/**
* 配置登录验证逻辑
*/
@Override
protected void configure(AuthenticationManagerBuilder auth){
//这里可启用我们自己的登陆验证逻辑
auth.authenticationProvider(userAuthenticationProvider);
}
/**
* 配置security的控制逻辑
* @Author Sans
* @CreateTime 2019/10/1 16:56
* @Param http 请求
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests
//不进行权限验证的请求或资源(从配置文件中读取)
.antMatchers(JWTConfig.antMatchers.split(",")).permitAll
//其他的需要登陆后才能访问
.anyRequest.authenticated
.and
//配置未登录自定义处理类
.httpBasic.authenticationEntryPoint(userAuthenticationEntryPointHandler)
.and
//配置登录地址
.formLogin
.loginProcessingUrl("/login/userLogin")
//配置登录成功自定义处理类
.successHandler(userLoginSuccessHandler)
//配置登录失败自定义处理类
.failureHandler(userLoginFailureHandler)
.and
//配置登出地址
.logout
.logoutUrl("/login/userLogout")
//配置用户登出自定义处理类
.logoutSuccessHandler(userLogoutSuccessHandler)
.and
//配置没有权限自定义处理类
.exceptionHandling.accessDeniedHandler(userAuthAccessDeniedHandler)
.and
// 开启跨域
.cors
.and
// 取消跨站请求伪造防护
.csrf.disable;
// 基于Token不需要session
http.sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 禁用缓存
http.headers.cacheControl;
// 添加JWT过滤器
http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager));
}
}

五.编写JWT拦截类

编写JWT接口请求校验拦截器

/**
* JWT接口请求校验拦截器
* 请求接口时会进入这里验证Token是否合法和过期
* @Author Sans
* @CreateTime 2019/10/5 16:41
*/
@Slf4j
public class JWTAuthenticationTokenFilter extends BasicAuthenticationFilter {
public JWTAuthenticationTokenFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 获取请求头中JWT的Token
String tokenHeader = request.getHeader(JWTConfig.tokenHeader);
if (!=tokenHeader && tokenHeader.startsWith(JWTConfig.tokenPrefix)) {
try {
// 截取JWT前缀
String token = tokenHeader.replace(JWTConfig.tokenPrefix, "");
// 解析JWT
Claims claims = Jwts.parser
.setSigningKey(JWTConfig.secret)
.parseClaimsJws(token)
.getBody;
// 获取用户名
String username = claims.getSubject;
String userId=claims.getId;
if(!StringUtils.isEmpty(username)&&!StringUtils.isEmpty(userId)) {
// 获取角色
List<GrantedAuthority> authorities = new ArrayList<>;
String authority = claims.get("authorities").toString;
if(!StringUtils.isEmpty(authority)){
List<Map<String,String>> authorityMap = JSONObject.parseObject(authority, List.class);
for(Map<String,String> role : authorityMap){
if(!StringUtils.isEmpty(role)) {
authorities.add(new SimpleGrantedAuthority(role.get("authority")));
}
}
}
//组装参数
SelfUserEntity selfUserEntity = new SelfUserEntity;
selfUserEntity.setUsername(claims.getSubject);
selfUserEntity.setUserId(Long.parseLong(claims.getId));
selfUserEntity.setAuthorities(authorities);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(selfUserEntity, userId, authorities);
SecurityContextHolder.getContext.setAuthentication(authentication);
}
} catch (ExpiredJwtException e){
log.info("Token过期");
} catch (Exception e) {
log.info("Token无效");
}
}
filterChain.doFilter(request, response);
return;
}
}

六.权限注解和hasPermission权限扩展

Security允许我们在定义URL方法访问所应有的注解权限时使用SpringEL表达式,在定义所需的访问权限时如果对应的表达式返回结果为true则表示拥有对应的权限,反之则没有权限,会进入到我们配置的UserAuthAccessDeniedHandler(暂无权限处理类)中进行处理.这里举一些例子,代码中注释有对应的描述.各种注解:SpringBoot 常用注解

 /**
* 管理端信息
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasRole('ADMIN')")
@RequestMapping(value = "/info",method = RequestMethod.GET)
public Map<String,Object> userLogin{
Map<String,Object> result = new HashMap<>;
SelfUserEntity userDetails = SecurityUtil.getUserInfo;
result.put("title","管理端信息");
result.put("data",userDetails);
return ResultUtil.resultSuccess(result);
}
/**
* 拥有ADMIN或者USER角色可以访问
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasAnyRole('ADMIN','USER')")
@RequestMapping(value = "/list",method = RequestMethod.GET)
public Map<String,Object> list{
Map<String,Object> result = new HashMap<>;
List<SysUserEntity> sysUserEntityList = sysUserService.list;
result.put("title","拥有用户或者管理员角色都可以查看");
result.put("data",sysUserEntityList);
return ResultUtil.resultSuccess(result);
}
/**
* 拥有ADMIN和USER角色可以访问
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasRole('ADMIN') and hasRole('USER')")
@RequestMapping(value = "/menuList",method = RequestMethod.GET)
public Map<String,Object> menuList{
Map<String,Object> result = new HashMap<>;
List<SysMenuEntity> sysMenuEntityList = sysMenuService.list;
result.put("title","拥有用户和管理员角色都可以查看");
result.put("data",sysMenuEntityList);
return ResultUtil.resultSuccess(result);
}

通常情况下使用hasRole和hasAnyRole基本可以满足大部分鉴权需求,但是有时候面对更复杂的场景上述常规表示式无法完成权限认证,Security也为我们提供了解决方案.通过hasPermission来扩展表达式.使用hasPermission首先要实现PermissionEvaluator接口

/**
* 自定义权限注解验证
* @Author Sans
* @CreateTime 2019/10/6 13:31
*/
@Component
public class UserPermissionEvaluator implements PermissionEvaluator {
@Autowired
private SysUserService sysUserService;
/**
* hasPermission鉴权方法
* 这里仅仅判断PreAuthorize注解中的权限表达式
* 实际中可以根据业务需求设计数据库通过targetUrl和permission做更复杂鉴权
* 当然targetUrl不一定是URL可以是数据Id还可以是管理员标识等,这里根据需求自行设计
* @Author Sans
* @CreateTime 2019/10/6 18:25
* @Param authentication 用户身份(在使用hasPermission表达式时Authentication参数默认会自动带上)
* @Param targetUrl 请求路径
* @Param permission 请求路径权限
* @Return boolean 是否通过
*/
@Override
public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
// 获取用户信息
SelfUserEntity selfUserEntity =(SelfUserEntity) authentication.getPrincipal;
// 查询用户权限(这里可以将权限放入缓存中提升效率)
Set<String> permissions = new HashSet<>;
List<SysMenuEntity> sysMenuEntityList = sysUserService.selectSysMenuByUserId(selfUserEntity.getUserId);
for (SysMenuEntity sysMenuEntity:sysMenuEntityList) {
permissions.add(sysMenuEntity.getPermission);
}
// 权限对比
if (permissions.contains(permission.toString)){
return true;
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return false;
}
}

在请求方法上添加hasPermission示例

 /**
* 拥有sys:user:info权限可以访问
* hasPermission 第一个参数是请求路径 第二个参数是权限表达式
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasPermission('/admin/userList','sys:user:info')")
@RequestMapping(value = "/userList",method = RequestMethod.GET)
public Map<String,Object> userList{
Map<String,Object> result = new HashMap<>;
List<SysUserEntity> sysUserEntityList = sysUserService.list;
result.put("title","拥有sys:user:info权限都可以查看");
result.put("data",sysUserEntityList);
return ResultUtil.resultSuccess(result);
}

hasPermission可以也可以和其他表达式联合使用

 /**
* 拥有ADMIN角色和sys:role:info权限可以访问
* @Author Sans
* @CreateTime 2019/10/2 14:22
* @Return Map<String,Object> 返回数据MAP
*/
@PreAuthorize("hasRole('ADMIN') and hasPermission('/admin/adminRoleList','sys:role:info')")
@RequestMapping(value = "/adminRoleList",method = RequestMethod.GET)
public Map<String,Object> adminRoleList{
Map<String,Object> result = new HashMap<>;
List<SysRoleEntity> sysRoleEntityList = sysRoleService.list;
result.put("title","拥有ADMIN角色和sys:role:info权限可以访问");
result.put("data",sysRoleEntityList);
return ResultUtil.resultSuccess(result);
}

七.测试

创建账户这里用户加密使用了Security推荐的bCryptPasswordEncoder方法

 /**
* 注册用户
*/
@Test
public void contextLoads {
// 注册用户
SysUserEntity sysUserEntity = new SysUserEntity;
sysUserEntity.setUsername("sans");
sysUserEntity.setPassword(bCryptPasswordEncoder.encode("123456"));
// 设置用户状态
sysUserEntity.setStatus("NORMAL");
sysUserService.save(sysUserEntity);
// 分配角色 1:ADMIN 2:USER
SysUserRoleEntity sysUserRoleEntity = new SysUserRoleEntity;
sysUserRoleEntity.setRoleId(2L);
sysUserRoleEntity.setUserId(sysUserEntity.getUserId);
sysUserRoleService.save(sysUserRoleEntity);
}

登录USER角色账号,登录成功后我们会获取到身份认证的Token

访问USER角色的接口,把上一步获取到的Token设置在Headers中,Key为Authorization,我们之前实现的JWTAuthenticationTokenFilter拦截器会根据请求头中的Authorization获取并解析Token

使用USER角色Token访问ADMIN角色的接口,会被拒绝,告知未授权(暂无权限会进入我们定义的UserAuthAccessDeniedHandler这个类进行处理)

更换ADMIN角色进行登录并访问ADMIN接口

今天就讲到这里,需要项目地址的可以留言。


之前,给大家发过三份Java面试宝典,这次新增了一份,目前总共是四份面试宝典,相信在跳槽前一个月按照面试宝典准备准备,基本没大问题。

  • 《java面试宝典5.0》(初中级)

  • 《350道Java面试题:整理自100+公司》(中高级)

  • 《资深java面试宝典-视频版》(资深)

  • 《Java[BAT]面试必备》(资深)

分别适用于初中级,中高级资深级工程师的面试复习。

内容包含java基础、javaweb、mysql性能优化、JVM、锁、百万并发、消息队列,高性能缓存、反射、Spring全家桶原理、微服务、Zookeeper、数据结构、限流熔断降级等等。

看到这里,证明有所收获

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码