使用SpringBoot和SpringSecurity实现 JWT 认证与授权
程序员文章站
2023-08-26 15:34:55
在学习时 JWT 写的Demo,在此记录方便复习Demo源码。使用 JWT ,简单来说就是将客户端传来的认证信息加密成 token,然后返回给客户端,客户端每个请求都携带上这个 token,服务器通过解析这个 token,完成对用户的认证授权。iJWT官网原理SpringSecurity 是使用一系列 Filter 来实现认证授权,所以我们只要编写自己的 Filter ,放在 FilterChain 的恰当位置。即可实现自己的认证授权规则。代码POM依赖 &...
在学习时 JWT 写的Demo,在此记录方便复习。Demo源码。使用 JWT ,简单来说就是将客户端传来的认证信息加密成 token,然后返回给客户端,客户端每个请求都携带上这个 token,服务器通过解析这个 token,完成对用户的认证授权。JWT官网
原理
SpringSecurity 是使用一系列 Filter 来实现认证授权,所以我们只要编写自己的 Filter ,放在 FilterChain 的恰当位置。即可实现自己的认证授权规则。
代码
- POM依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<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>
<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>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
-
项目结构
-
JWT 工具类
package com.example.jwtdemo.util;
import io.jsonwebtoken.*;
import java.util.Date;
import java.util.Map;
/**
* @author: leoi
* @create: 2021/3/3 11:45
*/
public class JwtUtils {
// 签发者
private static final String ISS = "leoiwan";
// 秘钥
public static final String SECRET = "secret";
// token前缀
public static final String TOKEN_PREFIX = "Bearer ";
// 请求头名称
public static final String HEADER_STRING = "Authentication";
// token有效时长
private static final long EXPIRATION = 60 * 60 * 3 * 1000L;
// 将 Map 转换为 JWT
public static String createToken(Map<String, Object> claims) {
return Jwts
.builder()
.signWith(SignatureAlgorithm.HS512, SECRET)
.setClaims(claims)
.setIssuer(ISS)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.compact();
}
// 将 JWT 转换为 Map
public static Claims parseToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {
e.printStackTrace();
}
return claims;
}
private JwtUtils() {
}
}
-
UserDetails 与 UserDetailsService
实现这个两个接口,可以让我们自定义用户认证。UserDetails 定义了如何获得用户名,密码,权限等;UserDetailsService 只有一个方法,那就是根据用户名得到 UserDetails 。SpringSecurity 源码如下:
UserDetails
public interface UserDetails extends Serializable { Collection<? extends GrantedAuthority> getAuthorities(); String getPassword(); String getUsername(); boolean isAccountNonExpired(); boolean isAccountNonLocked(); boolean isCredentialsNonExpired(); boolean isEnabled(); }
UserDetailsService
public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
具体实现:
UserInfo
package com.example.jwtdemo.entity; import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import java.util.Collection; import java.util.Collections; /** * @author: leoi * @create: 2021/3/3 11:58 */ @Data public class UserInfo implements UserDetails { private static final long serialVersionUID = 347318048229419086L; private final String username; public UserInfo(String username) { this.username = username; } @Override public String toString() { return "UserInfo{" + "username='" + username + "',\n" + "password='" + getPassword() + "',\n" + "authorities'=" + getAuthorities() + "',\n" + '}'; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")); } @Override public String getPassword() { return new BCryptPasswordEncoder().encode("123456"); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
UserServiceImpl
package com.example.jwtdemo.service; import com.example.jwtdemo.entity.UserInfo; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; /** * @author: leoi * @create: 2021/3/3 12:01 */ @Service public class UserServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return new UserInfo(username); } }
-
自定义 Filter
package com.example.jwtdemo.config; import com.example.jwtdemo.util.JwtUtils; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Date; /** * @author: leoi * @create: 2021/3/3 17:25 */ @Slf4j public class JwtAuthenticationFilter extends OncePerRequestFilter { @Qualifier("userServiceImpl") @Autowired private UserDetailsService userService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = request.getHeader(JwtUtils.HEADER_STRING); // 需要携带正确的前缀 if (token != null && token.startsWith(JwtUtils.TOKEN_PREFIX)) { // 解析 Jwt 并得到用户名 token = token.substring(JwtUtils.TOKEN_PREFIX.length()); log.info("checking token:{}", token); Claims jwtParams = JwtUtils.parseToken(token); String username = (String) jwtParams.get("subject"); log.info("checking username:{}", username); // 是否为有效 Token if (username != null && jwtParams.getExpiration().after(new Date())) { // 构建 authentication 对象 UserDetails userInfo = userService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userInfo.getUsername(), null, userInfo.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); log.info("authenticated User:{}", username); log.info("authenticated Authorities:{}", userInfo.getAuthorities()); // 放入 Security 上下文 SecurityContextHolder.getContext().setAuthentication(authentication); } } filterChain.doFilter(request, response); } }
-
Security配置
package com.example.jwtdemo.config; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import javax.servlet.http.HttpServletResponse; /** * @author: leoi * @create: 2021/3/3 12:08 */ @Slf4j @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired @Qualifier("userServiceImpl") private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf() .disable(); http.authorizeRequests() .antMatchers("/", "/login/**").permitAll() .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() .anyRequest().authenticated(); http.exceptionHandling() .authenticationEntryPoint((req, resp, e) -> { log.info("匿名用户访问无权限资源"); resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); }) .accessDeniedHandler((req, resp, e) -> { log.info("认证用户访问无权限资源"); resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); }); // 将自定义过滤器添加在 UsernamePasswordAuthenticationFilter 之后 http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } }
测试
-
登录测试
package com.example.jwtdemo.controller; import com.example.jwtdemo.util.JwtUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; /** * @author: leoi * @create: 2021/3/3 12:14 */ @RestController public class AuthenticationController { @Autowired @Qualifier("userServiceImpl") private UserDetailsService userService; @PostMapping("/login") public String login(@RequestParam String username, @RequestParam String password) { UserDetails userDetails = userService.loadUserByUsername(username); HashMap<String, Object> claims = new HashMap<String, Object>() { private static final long serialVersionUID = 3803002510172418126L; { put("subject", userDetails.getUsername()); put("password", userDetails.getPassword()); } }; return JwtUtils.createToken(claims); } }
-
获取 Token
-
获取认证对象
-
-
授权测试
package com.example.jwtdemo.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author: leoi * @create: 2021/3/3 12:42 */ @RestController @RequestMapping("/role") public class AuthorizationController { @GetMapping("/user") @PreAuthorize("hasAnyRole('ROLE_USER')") public String user() { return "access success,has role [user]"; } @GetMapping("/admin") @PreAuthorize("hasAnyRole('ROLE_ADMIN')") public String admin() { return "access success,has role [admin]"; } }
-
有权限的访问
-
无权限的访问
-
本文地址:https://blog.csdn.net/qq_43619402/article/details/114334538
上一篇: 关于网站开发中div标签中设置宽度后其中文本溢出的原因和解决方法
下一篇: 用JQ实现的一个简单轮播
推荐阅读
-
使用SpringBoot和SpringSecurity实现 JWT 认证与授权
-
Springboot+SpringSecurity+JWT实现用户登录和权限认证示例
-
asp.net core3.1cookie和jwt混合认证授权实现多种身份验证方案
-
SpringBoot+SpringSecurity实现基于真实数据的授权认证
-
springboot+sercuity+oauth2+Jwt+手机号+微信+密码 企业级认证与授权原理以及实现(完整版)
-
OAuth2 和 SpringSecurity完成授权认证中心(二) 通过jwt令牌,以及JDBC存储
-
Springboot+SpringSecurity+JWT实现用户登录和权限认证示例
-
使用shiro框架实现认证与授权
-
asp.net core3.1cookie和jwt混合认证授权实现多种身份验证方案
-
使用SpringBoot和SpringSecurity实现 JWT 认证与授权