Spring Security认证

Spring Security

Spring Security是主要解决认证(Authenticate)和授权(Authorization)的框架。

1、添加依赖

在Spring Boot项目中,添加spring-boot-starter-security依赖项。

<!-- Spring Security:处理认证与授权 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

注意:以上依赖项是带有自动配置的,一旦添加此依赖,整个项目中所有的访问,默认都是必须先登录才可以访问的,在浏览器输入任何此服务的URL,都会自动跳转到默认的登录页面。默认的用户名是user,默认的密码是启动项目时自动生成的随机密码,在服务器端的控制台可以看到此密码。当登录后,会自动跳转到此前尝试访问的页面。
Spring Security默认使用Session机制保存用户的登录状态,所以,重启服务后,登录状态会消失。在不重启的情况下,可以通过 /logout 访问“退出登录”页面,确定后也可以清除登录状态。

2、关于在Service中调用Security的认证机制:

当需要调用Security框架的认证机制时,需要使用AuthenticationManager对象,可以在Security配置类中重写authenticationManager()方法,在此方法上添加@Bean注解,由于当前类本身是配置类,所以Spring框架会自动调用此方法,并将返回的结果保存到Spring容器中:

@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
    return super.authenticationManager();
}

IAdminService中添加处理登录的抽象方法:

void login(AdminLoginDTO adminLoginDTO);

AdminServiceImpl中,可以自动装配AuthenticationManager对象:

@Autowired
private AuthenticationManager authenticationManager;

并实现接口中的方法:

@Override
public void login(AdminLoginDTO adminLoginDTO) {
    // 日志
    log.debug("开始处理【管理员登录】的业务,参数:{}", adminLoginDTO);
    // 调用AuthenticationManager执行认证
    Authentication authentication = new UsernamePasswordAuthenticationToken(
            adminLoginDTO.getUsername(), adminLoginDTO.getPassword());
    authenticationManager.authenticate(authentication);
    log.debug("认证通过!");
}

3、在控制器中接收登录请求,并调用Service:

在根包下创建pojo.dto.AdminLoginDTO类:

@Data
public class AdminLoginDTO implements Serializable {
    private String username;
    private String password;
}

AdminController中添加处理请求的方法:

@ApiOperation("管理员登录")
@ApiOperationSupport(order = 50)
@PostMapping("/login")
public JsonResult<Void> login(AdminLoginDTO adminLoginDTO) {
    log.debug("准备处理【管理员登录】的请求:{}", adminLoginDTO);
    adminService.login(adminLoginDTO);
    return JsonResult.ok();
}

为了保证能对以上路径直接发起请求,需要将此路径(/admins/login)添加到Security配置类的“白名单”中。
完成后,启动项目,可以通过Knife4j的调试来测试登录,当登录成功时将响应正确,当用户名或密码错误时,将响应错误(需要统一处理异常)。注意:即使登录成功,也不可以实现其它请求的访问!

4、处理登录成功的管理的权限列表

目前,存入到Security上下文中的认证信息(Authentication对象)并不包含有效的权限信息(目前是个假信息),为了后续能够判断用户的权限,需要:

  • 当认证(登录)成功后,取出管理员的权限,并将其存入到JWT数据中
  • 后续的请求中的JWT应该已经包含权限,则可以从JWT中解析出权限信息,并存入到认证信息(Authentication对象)中
  • 在操作过程中,应该先将权限列表转换成JSON再存入到JWT中,在解析JWT时,得到的权限信息也是一个JSON数据,需要将其转换成对象才能继续使用

关于JSON格式的转换,有许多工具都可以实现,例如:fastjson

<!-- fastjson:实现对象与JSON的相互转换 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>

AdminServiceImpl处理登录时,当认证成功时,需要从认证结果中取出权限列表,转换成JSON字符串,并存入到JWT中:

// 原有其它代码
Collection<GrantedAuthority> authorities = loginUser.getAuthorities();
log.debug("认证结果中的权限列表:{}", authorities);
String authorityListString = JSON.toJSONString(authorities); // 【重要】将权限列表转换成JSON格式,用于存储到JWT中

// 生成JWT时的Claims相关代码
claims.put("authorities", authorityListString);
log.debug("生成JWT,向JWT中存入authorities:{}", authorityListString);

5、处理异常

在登录时,可能出现:

  • 用户名错误:BadCredentialsException
  • 密码错误:BadCredentialsException
  • 账号被禁用:DisabledException
    在访问时,可能出现:
  • 无此权限:AccessDeniedException
    以上异常都可以由统一处理异常的机制进行处理,则先在ServiceCode中添加对应的业务状态码:
/**
 * 未授权的访问
 */
Integer ERR_UNAUTHORIZED = 40100;
/**
 * 未授权的访问:账号禁用
 */
Integer ERR_UNAUTHORIZED_DISABLED = 40101;
/**
 * 禁止访问,通常是已登录,但无权限
 */
Integer ERR_FORBIDDEN = 40300;

然后,在统一处理异常的类中,添加对相关异常的处理:

@ExceptionHandler
public JsonResult<Void> handleBadCredentialsException(BadCredentialsException e) {
    String message = "登录失败,用户名或密码错误!";
    log.debug("处理BadCredentialsException:{}", message);
    return JsonResult.fail(ServiceCode.ERR_UNAUTHORIZED, message);
}

@ExceptionHandler
public JsonResult<Void> handleDisabledException(DisabledException e) {
    String message = "登录失败,此账号已禁用!";
    log.debug("处理DisabledException:{}", message);
    return JsonResult.fail(ServiceCode.ERR_UNAUTHORIZED_DISABLED, message);
}

@ExceptionHandler
public JsonResult<Void> handleAccessDeniedException(AccessDeniedException e) {
    String message = "访问失败,当前登录的账号无此权限!";
    log.debug("处理AccessDeniedException:{}", message);
    return JsonResult.fail(ServiceCode.ERR_FORBIDDEN, message);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值