Browse Source

移除spring security,授权采用jwt token

master
xie.bx 3 years ago
parent
commit
28d9269432
  1. 6
      im-platform/pom.xml
  2. 3
      im-platform/src/main/java/com/bx/implatform/ImplatformApp.java
  3. 36
      im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java
  4. 172
      im-platform/src/main/java/com/bx/implatform/config/WebSecurityConfg.java
  5. 18
      im-platform/src/main/java/com/bx/implatform/controller/LoginController.java
  6. 7
      im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java
  7. 37
      im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java
  8. 5
      im-platform/src/main/java/com/bx/implatform/service/IUserService.java
  9. 26
      im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java
  10. 16
      im-platform/src/main/java/com/bx/implatform/session/SessionContext.java
  11. 94
      im-platform/src/main/java/com/bx/implatform/util/JwtUtil.java
  12. 22
      im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java
  13. 5
      im-ui/src/api/httpRequest.js
  14. 10
      im-ui/src/view/Home.vue
  15. 19
      im-ui/src/view/Login.vue

6
im-platform/pom.xml

@ -106,6 +106,12 @@
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.2</version>
</dependency>
<!-- 引入操作JWT的相关依赖 -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.11.0</version>
</dependency>
</dependencies>
<build>

3
im-platform/src/main/java/com/bx/implatform/ImplatformApp.java

@ -4,13 +4,14 @@ import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Slf4j
@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan(basePackages = {"com.bx.implatform.mapper"})
@SpringBootApplication
@SpringBootApplication(exclude= {SecurityAutoConfiguration.class })// 禁用secrity
public class ImplatformApp {
public static void main(String[] args) {

36
im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java

@ -0,0 +1,36 @@
package com.bx.implatform.config;
import com.bx.implatform.interceptor.AuthInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor())
.addPathPatterns("/**")
.excludePathPatterns( "/image/upload","/login","/logout","/register",
"/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
}
@Bean
public AuthInterceptor authInterceptor() {
return new AuthInterceptor();
}
@Bean
public PasswordEncoder passwordEncoder(){
// 使用BCrypt加密密码
return new BCryptPasswordEncoder();
}
}

172
im-platform/src/main/java/com/bx/implatform/config/WebSecurityConfg.java

@ -1,172 +0,0 @@
package com.bx.implatform.config;
import com.alibaba.fastjson.JSON;
import com.bx.implatform.enums.ResultCode;
import com.bx.implatform.result.Result;
import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.service.IUserService;
import com.bx.implatform.session.UserSession;
import com.fasterxml.jackson.databind.ObjectMapper;
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.security.authentication.*;
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.User;
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.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import java.io.PrintWriter;
/*
* SpringSecurity安全框架配置
*
* @Author Blue
* @Date 2022/10/21
*/
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfg extends WebSecurityConfigurerAdapter {
@Qualifier("securityUserDetailsServiceImpl")
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private IUserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/image/upload","/login","/logout","/register","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**")
.permitAll()
.anyRequest() //任何其它请求
.authenticated() //都需要身份认证
.and()
// 登录配置表单认证方式
.formLogin()
.usernameParameter("username")//设置登录账号参数,与表单参数一致
.passwordParameter("password")//设置登录密码参数,与表单参数一致
.loginProcessingUrl("/login")//配置默认登录入口
.successHandler(successHandler())
.failureHandler(failureHandler())
.and()
// 注销
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(logoutHandler())
.permitAll()
.and()
// session管理
.sessionManagement()
.and()
// 禁用跨站csrf攻击防御
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(entryPoint());
}
@Bean
AuthenticationFailureHandler failureHandler(){
return (request, response, exception) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = ResultUtils.error(ResultCode.LOGIN_ERROR,exception.getMessage());
if (exception instanceof LockedException) {
result =ResultUtils.error(ResultCode.LOGIN_ERROR,"账户被锁定,请联系管理员!");
} else if (exception instanceof CredentialsExpiredException) {
result = ResultUtils.error(ResultCode.LOGIN_ERROR,"密码过期,请联系管理员!");
} else if (exception instanceof AccountExpiredException) {
result =ResultUtils.error(ResultCode.LOGIN_ERROR,"账户过期,请联系管理员!");
} else if (exception instanceof DisabledException) {
result = ResultUtils.error(ResultCode.LOGIN_ERROR,"账户被禁用,请联系管理员!");
} else if (exception instanceof BadCredentialsException) {
result =ResultUtils.error(ResultCode.LOGIN_ERROR,"用户名或者密码输入错误,请重新输入!");
}
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
};
}
@Bean
AuthenticationSuccessHandler successHandler(){
return (request, response, authentication) -> {
User useDetail = (User)authentication.getPrincipal();
String strJson = useDetail.getUsername();
UserSession userSession = JSON.parseObject(strJson,UserSession.class);
log.info("用户 '{}' 登录,id:{},昵称:{}",userSession.getUserName(),userSession.getId(),userSession.getNickName());
// 响应
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = ResultUtils.success();
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
};
}
@Bean
LogoutSuccessHandler logoutHandler(){
return (request, response, authentication) -> {
User useDetail = (User)authentication.getPrincipal();
String strJson = useDetail.getUsername();
UserSession userSession = JSON.parseObject(strJson,UserSession.class);
log.info("用户 '{}' 退出,id:{},昵称:{}",userSession.getUserName(),userSession.getId(),userSession.getNickName());
// 响应
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = ResultUtils.success();
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
};
}
@Bean
AuthenticationEntryPoint entryPoint(){
return (request, response, exception) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = ResultUtils.error(ResultCode.NO_LOGIN);
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
};
}
@Bean
public PasswordEncoder passwordEncoder(){
// 使用BCrypt加密密码
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}

18
im-platform/src/main/java/com/bx/implatform/controller/RegisterController.java → im-platform/src/main/java/com/bx/implatform/controller/LoginController.java

@ -1,9 +1,11 @@
package com.bx.implatform.controller;
import com.bx.implatform.enums.ResultCode;
import com.bx.implatform.result.Result;
import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.service.IUserService;
import com.bx.implatform.vo.LoginVO;
import com.bx.implatform.vo.RegisterVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@ -16,17 +18,25 @@ import javax.validation.Valid;
@Api(tags = "用户注册")
@Api(tags = "用户登录和注册")
@RestController
public class RegisterController {
public class LoginController {
@Autowired
private IUserService userService;
@PostMapping("/login")
@ApiOperation(value = "用户注册",notes="用户注册")
public Result register(@Valid @RequestBody LoginVO vo){
String token = userService.login(vo);
return ResultUtils.success(token,ResultCode.SUCCESS.getMsg());
}
@PostMapping("/register")
@ApiOperation(value = "用户注册",notes="用户注册")
public Result register(@Valid @RequestBody RegisterVO dto){
userService.register(dto);
public Result register(@Valid @RequestBody RegisterVO vo){
userService.register(vo);
return ResultUtils.success();
}
}

7
im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java

@ -11,14 +11,9 @@ public enum ResultCode {
SUCCESS(200,"成功"),
LOGIN_ERROR(400,"登录异常"),
NO_LOGIN(401,"未登录"),
FORBIDDEN(403,"禁止访问"),
NOT_FIND(404,"无法找到文件"),
PROGRAM_ERROR(500,"系统繁忙,请稍后再试"),
PASSWOR_ERROR(10001,"密码不正确"),
VERITY_CODE_NOT_EXIST(10002,"验证码不存在"),
VERITY_CODE_ERROR(10003,"验证码不正确"),
USERNAME_ALREADY_REGISTER(10004,"该用户名已注册"),
MOBILE_ALREADY_REGISTER(10005,"该手机号码已注册"),
USERNAME_ALREADY_REGISTER(10003,"该用户名已注册"),
;

37
im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java

@ -0,0 +1,37 @@
package com.bx.implatform.interceptor;
import com.alibaba.fastjson.JSON;
import com.bx.implatform.enums.ResultCode;
import com.bx.implatform.exception.GlobalException;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.JwtUtil;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
;
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
//从 http 请求头中取出 token
String token = request.getHeader("token");
if (token == null) {
throw new GlobalException(ResultCode.NO_LOGIN);
}
//验证 token
JwtUtil.checkSign(token);
// 存放session
String strJson = JwtUtil.getInfo(token);
UserSession userSession = JSON.parseObject(strJson,UserSession.class);
request.setAttribute("session",userSession);
return true;
}
}

5
im-platform/src/main/java/com/bx/implatform/service/IUserService.java

@ -2,6 +2,7 @@ package com.bx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bx.implatform.entity.User;
import com.bx.implatform.vo.LoginVO;
import com.bx.implatform.vo.RegisterVO;
import com.bx.implatform.vo.UserVO;
@ -10,7 +11,9 @@ import java.util.List;
public interface IUserService extends IService<User> {
void register(RegisterVO registerDTO);
String login(LoginVO vo);
void register(RegisterVO vo);
User findUserByName(String username);

26
im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java

@ -1,5 +1,6 @@
package com.bx.implatform.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.imcommon.contant.RedisKey;
@ -15,6 +16,8 @@ import com.bx.implatform.service.IUserService;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.util.JwtUtil;
import com.bx.implatform.vo.LoginVO;
import com.bx.implatform.vo.RegisterVO;
import com.bx.implatform.vo.UserVO;
import lombok.extern.slf4j.Slf4j;
@ -45,6 +48,29 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
@Autowired
private IFriendService friendService;
/**
* 用户登录
*
* @param vo 注册vo
* @return
*/
@Override
public String login(LoginVO vo) {
User user = findUserByName(vo.getUserName());
if(null == user){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"用户不存在");
}
if(!passwordEncoder.matches(vo.getPassword(),user.getPassword())){
throw new GlobalException(ResultCode.PASSWOR_ERROR);
}
// 生成token
UserSession session = BeanUtils.copyProperties(user,UserSession.class);
String strJson = JSON.toJSONString(session);
String token = JwtUtil.sign(user.getId(),strJson);
return token;
}
/**
* 用户注册
*

16
im-platform/src/main/java/com/bx/implatform/session/SessionContext.java

@ -1,8 +1,9 @@
package com.bx.implatform.session;
import com.alibaba.fastjson.JSON;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/*
* @Description
@ -13,11 +14,12 @@ public class SessionContext {
public static UserSession getSession(){
User useDetail = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String strJson = useDetail.getUsername();
UserSession userSession = JSON.parseObject(strJson,UserSession.class);
// 从请求上下文里获取Request对象
ServletRequestAttributes requestAttributes = ServletRequestAttributes.class.
cast(RequestContextHolder.getRequestAttributes());
HttpServletRequest request = requestAttributes.getRequest();
UserSession userSession = (UserSession) request.getAttribute("session");
return userSession;
}
}

94
im-platform/src/main/java/com/bx/implatform/util/JwtUtil.java

@ -0,0 +1,94 @@
package com.bx.implatform.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.bx.implatform.exception.GlobalException;
;
import java.util.Date;
import java.util.Map;
public class JwtUtil {
/**
* 过期5分钟
* */
private static final long EXPIRE_TIME = 30 * 60 * 1000;
/**
* jwt密钥
* */
private static final String SECRET = "MIIBIjANBgkq";
/**
* 生成jwt字符串30分钟后过期 JWT(json web token)
* @param userId
* @param info
* @return
* */
public static String sign(Long userId, String info) {
try {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(SECRET);
return JWT.create()
//将userId保存到token里面
.withAudience(userId.toString())
//存放自定义数据
.withClaim("info", info)
//五分钟后token过期
.withExpiresAt(date)
//token的密钥
.sign(algorithm);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据token获取userId
* @param token
* @return
* */
public static Long getUserId(String token) {
try {
String userId = JWT.decode(token).getAudience().get(0);
return Long.parseLong(userId);
}catch (JWTDecodeException e) {
return null;
}
}
/**
* 根据token获取自定义数据info
* @param token
* @return
* */
public static String getInfo(String token) {
try {
return JWT.decode(token).getClaim("info").asString();
}catch (JWTDecodeException e) {
return null;
}
}
/**
* 校验token
* @param token
* @return
* */
public static boolean checkSign(String token) {
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTVerifier verifier = JWT.require(algorithm)
//.withClaim("username, username)
.build();
verifier.verify(token);
return true;
}catch (JWTVerificationException e) {
throw new GlobalException("token 无效,请重新获取");
}
}
}

22
im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java

@ -0,0 +1,22 @@
package com.bx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
@Data
@ApiModel("用户登录VO")
public class LoginVO {
//@NotEmpty(message="用户名不可为空")
@ApiModelProperty(value = "用户名")
private String userName;
// @NotEmpty(message="用户密码不可为空")
@ApiModelProperty(value = "用户密码")
private String password;
}

5
im-ui/src/api/httpRequest.js

@ -15,7 +15,10 @@ const http = axios.create({
* 请求拦截
*/
http.interceptors.request.use(config => {
// todo 请求头带上token
let token = sessionStorage.getItem("token");
if (token) {
config.headers.token = sessionStorage.getItem("token");
}
return config
}, error => {
return Promise.reject(error)

10
im-ui/src/view/Home.vue

@ -190,13 +190,9 @@
this.playAudioTip();
},
handleExit() {
this.$http({
url: "/logout",
method: 'get'
}).then(() => {
this.$wsApi.closeWebSocket();
location.href = "/";
})
this.$wsApi.closeWebSocket();
sessionStorage.removeItem("token");
location.href = "/";
},
playAudioTip(){
let audio = new Audio();

19
im-ui/src/view/Login.vue

@ -2,8 +2,8 @@
<div class="login-view" >
<el-form :model="loginForm" status-icon :rules="rules" ref="loginForm" label-width="60px" class="web-ruleForm" @keyup.enter.native="submitForm('loginForm')">
<div class="login-brand">欢迎登陆</div>
<el-form-item label="用户名" prop="username">
<el-input type="username" v-model="loginForm.username" autocomplete="off"></el-input>
<el-form-item label="用户名" prop="userName">
<el-input type="userName" v-model="loginForm.userName" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
@ -42,11 +42,11 @@
};
return {
loginForm: {
username: '',
userName: '',
password: ''
},
rules: {
username: [{
userName: [{
validator: checkUsername,
trigger: 'blur'
}],
@ -64,11 +64,14 @@
this.$http({
url: "/login",
method: 'post',
params: this.loginForm
data: this.loginForm
})
.then((data) => {
this.setCookie('username',this.loginForm.username);
.then((token) => {
// cookie(便)
this.setCookie('username',this.loginForm.userName);
this.setCookie('password',this.loginForm.password);
// token
sessionStorage.setItem("token",token);
this.$message.success("登陆成功");
this.$router.push("/home/chat");
})
@ -105,7 +108,7 @@
}
},
mounted() {
this.loginForm.username = this.getCookie("username");
this.loginForm.userName = this.getCookie("username");
// cookie便
this.loginForm.password = this.getCookie("password");
}

Loading…
Cancel
Save