SpringSecurity系列——基于SpringBoot2.7的登录接口
- 完整登录
-
- 本文所用JWTUtil地址
- 环境
- 准备工作
-
- 目录
- 数据库
- pom.xml
- 实体类User
- UserMapper
- LoginService
- ResultUtil统一封装返回
- LoginParam前端表单接收
- JWTUtil
- LoginController
- SpringSecurity配置
-
- SpringSecurityConfig
- UserDetailsServiceImpl
- UserLogin(UserDetails的实现类)
- LoginServiceImpl
-
- 关于为什么获取用户信息使用authenticate.getPrincipal()方法
- 使用ApiFox测试
完整登录
本文所用JWTUtil地址
这个JWT工具类是我自己写的,具体怎么使用都在README.md中了
高度封装使用简单
git@gitee.com:giteeforsyf/jjwtutil.git
环境
SpringBoot版本:2.7
SpringSecurity版本:5.4+
注:SpringSecurityConfigurationAdapter已过时
准备工作
在准备工作中我们需要准备数据库、实体类、配置、工具类等
目录
数据库
数据库设计如图:
pom.xml
<dependencies>
<dependency>
<groupId>cn.fly</groupId>
<artifactId>JJWTUtil</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.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.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</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>
实体类User
package com.example.ss1.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
@TableName("user")
public class User {
private Long id;
private String username;
private String password;
private String status;
private String nickname;
}
UserMapper
package com.example.ss1.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.ss1.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
LoginService
package com.example.ss1.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.ss1.entity.User;
import com.example.ss1.parm.LoginParam;
import com.example.ss1.util.ResultUtil;
public interface LoginService extends IService<User> {
ResultUtil login(LoginParam loginParam);
}
ResultUtil统一封装返回
package com.example.ss1.util;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultUtil {
private int code;
private String Message;
private Object data;
public static ResultUtil success(int code, String msg, Object data) {
ResultUtil ResultUtil = new ResultUtil();
ResultUtil.setCode(200);
ResultUtil.setMessage(msg);
ResultUtil.setData(data);
return ResultUtil;
}
public static ResultUtil success() {
return success(200, null, null);
}
public static ResultUtil success(String msg) {
return success(200, msg, null);
}
public static ResultUtil success(String msg, Object data) {
return success(200, msg, data);
}
public static ResultUtil fail(int code, String msg, Object data) {
ResultUtil ResultUtil = new ResultUtil();
ResultUtil.setCode(code);
ResultUtil.setMessage(msg);
ResultUtil.setData(data);
return ResultUtil;
}
public static ResultUtil fail(int code) {
return success(code, null, null);
}
public static ResultUtil fail(int code, String msg) {
return success(code, msg, null);
}
public static ResultUtil fail() {
return success(404, null, null);
}
}
LoginParam前端表单接收
package com.example.ss1.parm;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class LoginParam implements Serializable {
private String username;
private String password;
}
JWTUtil
这里解释一下,由于我直接使用了我自己写的JJWTUtil所以这里只要将该类是设置为配置类继承JJWTUtil即可
package com.example.ss1.util;
import cn.fly.jjwtutil.JJWTUtil;
import lombok.Data;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class JWTUtil extends JJWTUtil {
}
LoginController
package com.example.ss1.controller;
import com.example.ss1.parm.LoginParam;
import com.example.ss1.service.LoginService;
import com.example.ss1.util.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {
@Autowired
private LoginService loginService;
@PostMapping("/user/login")
public ResultUtil login(@RequestBody LoginParam loginParam){
return loginService.login(loginParam);
}
}
SpringSecurity配置
SpringSecurityConfig
对SpringSecurity的配置类,这里面编译器提示无法自动装配不用管
package com.example.ss1.config;
import com.example.ss1.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@Configuration
public class SpringSecurityConfig {
@Autowired
private UserDetailsServiceImpl userDetailsService;
//获取AuthenticationManager(认证管理器),登录时认证使用
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
//配置过滤
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.csrf().disable()//关闭csrf
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//关闭session
.and()
.authorizeRequests(auth ->
auth.antMatchers("/**").permitAll()
.anyRequest().authenticated()
)
.userDetailsService(userDetailsService).build();
}
//配置加密方式
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//配置跨源访问(CORS)
@Bean
CorsConfigurationSource corsConfigurationSource(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
UserDetailsServiceImpl
package com.example.ss1.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.ss1.entity.User;
import com.example.ss1.entity.UserLogin;
import com.example.ss1.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
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;
import java.util.Objects;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//查询用户信息
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUsername, username).last("limit 1");
User user = userMapper.selectOne(queryWrapper);
//异常
if (Objects.isNull(user)){
throw new UsernameNotFoundException("用户名未发现");
}
//查询对应权限信息
//数据封装为UserDetails返回
return new UserLogin(user);
}
}
UserLogin(UserDetails的实现类)
由于我们需要在UserDetailsServiceImpl中将数据进行封装
所以我们就需要有这样一个类实现UserDetails接口,定义返回的一系列方法
package com.example.ss1.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserLogin implements UserDetails {
//传入用户对象
private User user;
/**
* 判断权限信息
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
/**
* 判断是否未过期
* @return
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 判断账户是否未锁定
* @return
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 判断是否可以使用
* @return
*/
@Override
public boolean isEnabled() {
return true;
}
}
LoginServiceImpl
package com.example.ss1.service.impl;
import cn.fly.jjwtutil.JJWTUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.ss1.entity.User;
import com.example.ss1.entity.UserLogin;
import com.example.ss1.mapper.UserMapper;
import com.example.ss1.parm.LoginParam;
import com.example.ss1.service.LoginService;
import com.example.ss1.util.JWTUtil;
import com.example.ss1.util.ResultUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import java.util.Objects;
@Service
public class LoginServiceImpl extends ServiceImpl<UserMapper, User> implements LoginService{
@Autowired
private JJWTUtil jjwtUtil;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public ResultUtil login(LoginParam loginParam) {
//进行用户认证。获取AuthenticationManager authenticate
//获取认证对象
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginParam.getUsername(), loginParam.getPassword());
//认证
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
//认证失败
if (Objects.isNull(authenticate)){
throw new RuntimeException("登录失败");
}
//认证成功,生成token
//获取用户信息(getPrincipal())
UserLogin user = (UserLogin) authenticate.getPrincipal();
Long id = user.getUser().getId();
Claims claims = Jwts.claims();
claims.put("userId",id);
jjwtUtil.defaultBuilder(jjwtUtil);
String token = jjwtUtil.createToken(claims);
System.out.println(token);
//返回
return ResultUtil.success("登录成功",token);
}
}
关于为什么获取用户信息使用authenticate.getPrincipal()方法
以下是调试结果,可以看到返回的用户信息是在Principal里的
其实在官网里很明确的说了
使用ApiFox测试
版权声明:程序员胖胖胖虎阿 发表于 2022年10月3日 下午7:48。
转载请注明:SpringSecurity系列——基于SpringBoot2.7的登录接口(内有惊喜)day2-1 | 胖虎的工具箱-编程导航
转载请注明:SpringSecurity系列——基于SpringBoot2.7的登录接口(内有惊喜)day2-1 | 胖虎的工具箱-编程导航
相关文章
暂无评论...