轻松上手SpringBoot+SpringSecurity+JWT实RESTfulAPI权限控制实战

我们知道在项目开发中,后台开发权限认证是非常重要的,springboot 中常用熟悉的权限认证框架有,shiro,还有就是springboot 全家桶的 security当然他们各有各的好处,但是我比较喜欢springboot自带的权限认证框架

<!--springboot 权限认证--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>

与springboot天然集成,功能强大

快速上手

主要实现 Spring Security 的安全认证,结合 RESTful API 的风格,使用无状态的环境。

主要实现是通过请求的 URL ,通过过滤器来做不同的授权策略操作,为该请求提供某个认证的方法,然后进行认证,授权成功返回授权实例信息,供服务调用。

基于Token的身份验证的过程如下:

用户通过用户名和密码发送请求。
程序验证。
程序返回一个签名的token 给客户端。
客户端储存token,并且每次用于每次发送请求。
服务端验证token并返回数据。
每一次请求都需要token,所以每次请求都会去验证用户身份,所以这里必须要使用缓存,

流程图

JWT JSON Web Token 验证流程图

轻松上手SpringBoot+SpringSecurity+JWT实RESTfulAPI权限控制实战

添加Spring Security和JWT依赖项

<!--springboot 权限认证--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!--jwt 认证--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> 生成JWT toke

因为要生成JWT toke 所以就写了一个工具类JwtTokenUtil

package cn.soboys.kmall.security.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.function.Function; /** * @author kenx * @version 1.0 * @date 2021/8/5 22:28 * @webSite https://www.soboys.cn/ */ @Component public class JwtTokenUtil implements Serializable { private static final long serialVersionUID = -2550185165626007488L; public static final long JWT_TOKEN_VALIDITY = 7*24*60*60; private String secret="TcUF7CC8T3txmfQ38pYsQ3KY"; public String getUsernameFromToken(String token) { return getClaimFromToken(token, Claims::getSubject); } public String generateToken(String username) { Map<String, Object> claims = new HashMap<>(); return doGenerateToken(claims, username); } public Boolean validateToken(String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) { final Claims claims = getAllClaimsFromToken(token); return claimsResolver.apply(claims); } private Claims getAllClaimsFromToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date()); } public Date getExpirationDateFromToken(String token) { return getClaimFromToken(token, Claims::getExpiration); } private String doGenerateToken(Map<String, Object> claims, String subject) { return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY*1000)).signWith(SignatureAlgorithm.HS512, secret).compact(); } } 注入数据源

这里我们使用数据库作为权限控制数据保存,所以就要注入数据源,进行权限认证
Spring Security提供了 UserDetailsService接口 用于用户身份认证,和UserDetails实体类,用于保存用户信息,(用户凭证,权限等)

看源码

package org.springframework.security.core.userdetails; public interface UserDetailsService { UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException; } package org.springframework.security.core.userdetails; public interface UserDetails extends Serializable { Collection<? extends GrantedAuthority> getAuthorities(); String getPassword(); String getUsername(); boolean isAccountNonExpired(); boolean isAccountNonLocked(); boolean isCredentialsNonExpired(); boolean isEnabled(); }

所以我们分为两步走:

自己的User实体类继承Spring SecurityUserDetails 保存相关权限信息

package cn.soboys.kmall.security.entity; 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 com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.io.Serializable; import java.time.LocalDateTime; import java.util.*; /** * <p> * 用户表 * </p> * * @author kenx * @since 2021-08-06 */ @Data @EqualsAndHashCode(callSuper = false) @TableName("t_user") public class User implements Serializable, UserDetails { private static final long serialVersionUID = 1L; /** * 用户ID */ @TableId(value = "USER_ID", type = IdType.AUTO) private Long userId; /** * 用户名 */ @TableField("USERNAME") private String username; /** * 密码 */ @TableField("PASSWORD") private String password; /** * 部门ID */ @TableField("DEPT_ID") private Long deptId; /** * 邮箱 */ @TableField("EMAIL") private String email; /** * 联系电话 */ @TableField("MOBILE") private String mobile; /** * 状态 0锁定 1有效 */ @TableField("STATUS") private String status; /** * 创建时间 */ @TableField("CREATE_TIME") private Date createTime; /** * 修改时间 */ @TableField("MODIFY_TIME") private Date modifyTime; /** * 最近访问时间 */ @TableField("LAST_LOGIN_TIME") private Date lastLoginTime; /** * 性别 0男 1女 2保密 */ @TableField("SSEX") private String ssex; /** * 是否开启tab,0关闭 1开启 */ @TableField("IS_TAB") private String isTab; /** * 主题 */ @TableField("THEME") private String theme; /** * 头像 */ @TableField("AVATAR") private String avatar; /** * 描述 */ @TableField("DESCRIPTION") private String description; @TableField(exist = false) private List<Role> roles; @TableField(exist = false) private Set<String> perms; /** * 用户权限 * * @return */ /*@Override public Collection<? extends GrantedAuthority> getAuthorities() { List<GrantedAuthority> auths = new ArrayList<>(); List<Role> roles = this.getRoles(); for (Role role : roles) { auths.add(new SimpleGrantedAuthority(role.getRolePerms())); } return auths; }*/ @Override @JsonIgnore public Collection<? extends GrantedAuthority> getAuthorities() { List<GrantedAuthority> auths = new ArrayList<>(); Set<String> perms = this.getPerms(); for (String perm : perms) { //这里perms值如果为空或空字符会报错 auths.add(new SimpleGrantedAuthority(perm)); } return auths; } @Override @JsonIgnore public boolean isAccountNonExpired() { return true; } @Override @JsonIgnore public boolean isAccountNonLocked() { return true; } @Override @JsonIgnore public boolean isCredentialsNonExpired() { return true; } @Override @JsonIgnore public boolean isEnabled() { return true; } }

注意这里有一个问题 登录用户时,总提示 User account is locked

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypwzj.html