1:前言
2:项目介绍
3:统一返回结果
4:登录功能实现
前言
简单介绍一个写这个博客的目的。
因为之前学开发都是学完所需的知识点再去做项目,但是这时候在做项目的过程中发现以前学过的全忘了,所以为了减少这种情况,鄙人打算以后通过项目学习技术,说的直接点就是,项目中需要用到哪些技术,那我就去学哪些技术,并用到此项目中。
项目介绍
这是一个SpringBoot + Vue 的分布式前后端项目,具体技术边学边用,因此等此项目完结了,这个项目介绍也就完结了。
目前用到的技术:
数据库:MySql
前端: Vue, Element-ui, LocalStorge
后端:Spring, SpringMvc, Mybatis-Plus, SptingBoot, SpringSecurity
统一返回结果
package com.monkey.monkeybackend.utils.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResultVO {
private int code;
private String msg;
private Object data;
}
package com.monkey.monkeybackend.utils.result;
public class ResultStatus {
public static final int OK=10000;
public static final int NO=10001; // 添加购物车失败
}
登录功能我选则的是Jwt_Token实现,将生成的Token存到本地游览器LocalStorge中 下面介绍Token实现的流程
几个实现登录的工具类
先引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
1:检测Token是否过期工具类
package com.monkey.monkeybackend.config.SpringSecurity;
import com.monkey.monkeybackend.Mapper.User.UserMapper;
import com.monkey.monkeybackend.Pojo.user.User;
import io.jsonwebtoken.Claims;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
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;
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private UserMapper userMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
// 从哪里读取token
String token = request.getHeader("Authorization");
// token以Bearer开头
if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
token = token.substring(7);
String userid;
try {
Claims claims = JwtUtil.parseJWT(token);
userid = claims.getSubject();
} catch (Exception e) {
throw new RuntimeException(e);
}
User user = userMapper.selectById(Integer.parseInt(userid));
if (user == null) {
throw new RuntimeException("用户名未登录");
}
UserDetailsImpl loginUser = new UserDetailsImpl(user);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, null);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
}
}
2: 生成Token类
package com.monkey.monkeybackend.config.SpringSecurity;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
@Component
public class JwtUtil {
public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 14; // 有效期14天
public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds121232131afasdfac";
public static String getUUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
public static String createJWT(String subject) {
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
return builder.compact();
}
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
SecretKey secretKey = generalKey();
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if (ttlMillis == null) {
ttlMillis = JwtUtil.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
return Jwts.builder()
.setId(uuid)
.setSubject(subject)
.setIssuer("sg")
.setIssuedAt(now)
.signWith(signatureAlgorithm, secretKey)
.setExpiration(expDate);
}
public static SecretKey generalKey() {
byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
}
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(jwt)
.getBody();
}
}
3:路径拦截器
package com.monkey.monkeybackend.config.SpringSecurity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
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.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/user/login", "/user/register", "/user/getUserInfoBytoken").permitAll()
.antMatchers("/blog/article/getArticleContentByLabelId", "/blog/article/pagination",
"/blog/article/fireRecently").permitAll()
.antMatchers("/blog/label/getLabelList").permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
}
4:得到用户信息工具类
package com.monkey.monkeybackend.config.SpringSecurity;
import com.monkey.monkeybackend.Pojo.user.User;
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 UserDetailsImpl implements UserDetails {
private User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
5:
package com.monkey.monkeybackend.config.SpringSecurity;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.monkey.monkeybackend.Mapper.User.UserMapper;
import com.monkey.monkeybackend.Pojo.user.User;
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;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("username", username);
User user = userMapper.selectOne(userQueryWrapper);
if (user == null) {
throw new RuntimeException("用户不存在");
}
return new UserDetailsImpl(user);
}
}
用户注册实现
// 用户注册
@Override
public ResultVO userRegister(Map<String, String> userInfo) {
String username = userInfo.get("username");
String password = userInfo.get("password");
String confirePassword = userInfo.get("confirePassword");
username = username.trim(); // 删除首位空白字符
if (username.length() == 0) {
return new ResultVO(ResultStatus.NO, "用户名不能为空", null);
}
if (password == null || password.length() == 0) {
return new ResultVO(ResultStatus.NO, "密码不能为空", null);
}
if (username.length() > 20) {
return new ResultVO(ResultStatus.NO, "用户名长度不能大于20", null);
}
if (password.length() > 20) {
return new ResultVO(ResultStatus.NO, "密码长度不能大于20", null);
}
if (!password.equals(confirePassword)) {
return new ResultVO(ResultStatus.NO, "两次密码不一致", null);
}
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("username", username);
Long selectCount = userMapper.selectCount(userQueryWrapper);
if (selectCount > 0) {
return new ResultVO(ResultStatus.NO, "该用户名已存在,请重新输入", null);
}
String encode = passwordEncoder.encode(password);
String photo = "https://cdn.acwing.com/media/user/profile/photo/246711_md_08990849f1.png";
User user = new User();
user.setPassword(encode);
user.setPhoto(photo);
user.setUsername(username);
user.setRegisterTime(new Date());
int insert = userMapper.insert(user);
if (insert > 0) {
return new ResultVO(ResultStatus.OK, "注册成功", null);
}
return new ResultVO(ResultStatus.OK, "注册失败", null);
}
用户登录实现
// 用户登录
@Override
public ResultVO userLogin(Map<String, String> userInfo) {
String username = userInfo.get("username");
String password = userInfo.get("password");
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = // 将用户名与密码封装成一个加密之后的字符串
new UsernamePasswordAuthenticationToken(username, password);
Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken); // 登录失败自动处理
UserDetailsImpl userDetails = (UserDetailsImpl) authenticate.getPrincipal();
User user = userDetails.getUser();
String token = JwtUtil.createJWT(user.getId().toString());
return new ResultVO(ResultStatus.OK, "登录成功", token);
}
用户登录前端实现
LoginViews.vue
loginUser() {
const vue = this;
store.dispatch("login", {
username: this.userInformation.username,
password: this.userInformation.password,
success() {
store.dispatch("getUserInfoBytoken", {
success() {
vue.$modal.msgSuccess("登录成功");
router.push({
name: "home",
});
},
error() {
vue.$modal.msgError("登录失败");
}
})
},
error(response) {
vue.$modal.msgError(response.msg)
}
})
}
store.user.js
login(context, data) {
$.ajax({
url: "http://localhost:4000/user/login",
type: "post",
data: {
username: data.username,
password: data.password
},
success(response) {
if (response.code == "10000") {
localStorage.setItem("token", response.data);
context.commit("updateToken", response.data);
data.success(response);
} else {
data.error(response);
}
},
error(response) {
data.error(response);
}
})
},
// 通过token得到用户信息
getUserInfoBytoken(context, data) {
$.ajax({
url: "http://localhost:4000/user/getUserInfoBytoken",
type: "get",
headers: {
Authorization: "Bearer " + context.state.token,
},
success(response) {
console.log(response)
if (response.code == "10000") {
context.commit("updateUserInfo", {
...response.data,
is_login: true,
});
if (data != null) data.success(response)
} else {
if (data != null) data.error(response);
}
},
errror(response) {
data.error(response);
}
})
},