SpringBoot整合SpringSecurity+Jwt
- 前言
- 一、引入jar包
- 二、使用步骤
- 1.流程图
- 2.工程目录
- 三、详细代码
- config类
- entity类
- controller类
- 关键类:SecurityUtil
- service类
- 四、启动项目测试
- 五、总结
前言
最近公司开启了一个新项目,登录和权限认证这一块我准备采用SpringSecurity+Jwt令牌来完成,下面是简单记录前期整合的部分,因为自己已经对这一块进行了一段时间的学习,文章只是对自己开发功能的一个记录,这两者没有分开细说,直接整合,只对一些必要代码进行补充。
PS:本来准备使用SpringSecurityOauth2.0的,仔细的考虑了一下系统的功能,虽然是分布式系统,但是主要还是自己系统内部的访问为主,并不对接三方,相比于SpringSecurityOauth2.0较为复杂的配置(主要还是自己不怎么熟0.0)SpringSecurity配置更加简单,所以直接上!
提示:以下是本篇文章正文,内容仅供参考
一、引入jar包
<!-- 具体版本自己选择 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${version}</version>
</dependency>
二、使用步骤
1.流程图
先看一张流程分析图:
主要流程简单概括如下:
1、用户提交用户名密码,SpringSecurity用UsernamePasswordAuthenticationToken封装用户名密码
继承关系如下图:
2、提交认证管理器
3、认证成功以后返回一个Authentication对象
4、Authentication对象填充到SecurityContextHolder安全上下文容器
5、根据Authentication生成token返回
2.工程目录
说明,因为前后端分离,没有配置登录页和跳转之类的,全部交由前端完成,所以mvc的配置是没有的。
三、详细代码
config类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author zhangqingfeng
* @version 1.0
* @email zhangqingfeng95@icloud.com
* @date 2021/4/19 11:12
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//SpringSecurity要求在定义认证逻辑是Spring容器内必须要有一个密码编码器,所以要提前注入一个Bean
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//低版本的时候认证管理器直接使用Autowired是可以注入的,
//但是这个版本一直报空指针一场,随后百度了一下,要自己重写一下方法才行
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/userLogin/login").permitAll()
.anyRequest().authenticated()
.and()
;
}
}
entity类
1、JwtAuthorization
/**
* 这个类是用来封装一些额外的请求参数到UsernamePasswordAuthenticationToken一起认证的,
* 因为前期整合,暂时并没有用到,只是预留在此
*/
public class JwtAuthorization extends UsernamePasswordAuthenticationToken {
public JwtAuthorization(Object principal, Object credentials) {
super(principal, credentials);
}
public JwtAuthorization(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}
2、user类
/**
* 此类同理,用来扩展信息,暂时只用到用户名密码和权限等字段
*/
public class User implements UserDetails {
private String username;
private String password;
private List<GrantedAuthority> grantedAuthorities;
public User(String username, String password, List<GrantedAuthority> grantedAuthorities) {
this.username = username;
this.password = password;
this.grantedAuthorities = grantedAuthorities;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public List<GrantedAuthority> getGrantedAuthorities() {
return grantedAuthorities;
}
public void setGrantedAuthorities(List<GrantedAuthority> grantedAuthorities) {
this.grantedAuthorities = grantedAuthorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.grantedAuthorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
controller类
@RestController
@CrossOrigin
@RequestMapping("/userLogin")
public class UserController {
//认证管理器
@Autowired
private AuthenticationManager authenticationManager;
@RequestMapping("/login")
public Result login(@RequestParam String username, @RequestParam String password){
String token = SecurityUtil.login(username, password,authenticationManager);
return new Result(true, StatusCode.OK,"登录成功!",token);
}
}
关键类:SecurityUtil
package com.kangjiu.utils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author zhangqingfeng
* @version 1.0
* @email zhangqingfeng95@icloud.com
* @date 2021/4/19 14:31
*/
@Configuration
@ComponentScan
public class SecurityUtil implements Serializable {
/**
* 有效期12小时
*/
private static final long EXPIRE_TIME = 12 * 60 * 60 * 1000;
/**
* 密钥
*/
private static final String SECRET = "abcdefg";
/**
* 创建时间
*/
private static final String CREATED = "created";
public static String login(String username, String password, AuthenticationManager authenticationManager){
//这里封装用户名密码
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,password);
//这里认证管理器最终会找到我们下面写的UserDetailService
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
return generateToken(authenticate);
}
/**
* 生成令牌
* 用户名权限等信息封装成令牌,后期再网关或者aop中解析
* @param authentication 用户
* @return 令牌
*/
public static String generateToken(Authentication authentication) {
Map<String, Object> claims = new HashMap<>(3);
claims.put("USERNAME", SecurityUtil.getUsername(authentication));
claims.put(CREATED, new Date());
claims.put("AUTHORITIES", authentication.getAuthorities());
return generateToken(claims);
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private static String generateToken(Map<String, Object> claims) {
Date expirationDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET).compact();
}
/**
* 获取用户名
* @return
*/
public static String getUsername(Authentication authentication) {
String username = null;
if(authentication != null) {
Object principal = authentication.getPrincipal();
if(principal != null && principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
}
}
return username;
}
public static Authentication getAuthentication() {
if(SecurityContextHolder.getContext() == null) {
return null;
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication;
}
}
service类
@Service
public class UserDetailServiceImpl implements UserDetailsService {
//密码编码器
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//这里后期要匹配数据库,从数据库里查询用户名密码,调用封装成User对象,和Authentication相比较
System.out.println("username为:"+s);
String username = "admin";
String password = "admin";
return new User(username,passwordEncoder.encode(password), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
四、启动项目测试
五、总结
以上就是我在项目里对SpringSeurity的整合了,如果有不对的地方,评论区请指正,谢谢!