• 首页
  • 栏目
  • CRM
  • 基于 SpringBoot + MyBatis 前后端分离实现的在线办公系统

基于 SpringBoot + MyBatis 前后端分离实现的在线办公系统

  • 2022-01-19
  • Admin

点击关注公众号,回复“1024”获取2TB学习资源!

1.开发环境的搭建及项目介绍

本项目目的是实现中小型企业的在线办公系统,云E办在线办公系统是一个用来管理日常的办公事务的一个系统。

使用SpringSecurity做安全认证及权限管理,Redis做缓存,RabbitMq做邮件的发送,使用EasyPOI实现对员工数据的导入和导出,使用WebSocket做在线聊天。

使用验证码登录

423be928e64f1df01c6fca6e267fadcc.png

页面展示:

f12a1d90d914de4f5c6fc8c6062e4157.png
  • 添加依赖

  • 使用MyBatis的AutoGenerator自动生成mapper,service,Controller

2.登录模块及配置框架搭建

<1>Jwt工具类及对Token的处理
  • 根据用户信息生成Token

定义JWT负载中用户名的Key以及创建时间的Key

  1. //用户名的key
  2. private static final String CLAIM_KEY_USERNAME="sub";
  3. //签名的时间
  4. private static final String CLAIM_KEY_CREATED="created";

从配置文件中拿到Jwt的密钥和失效时间

  1. /**
  2.  * @Value的值有两类:
  3.  * ① ${ property : default_value }
  4.  * ② #{ obj.property? :default_value }
  5.  * 第一个注入的是外部配置文件对应的property,第二个则是SpEL表达式对应的内容。 那个
  6.  * default_value,就是前面的值为空时的默认值。注意二者的不同,#{}里面那个obj代表对象。
  7.  */
  8. //JWT密钥
  9. @Value("${jwt.secret}")
  10. private  String secret;
  11. //JWT失效时间
  12. @Value("${jwt.expiration}")
  13. private Long expiration;

根据用户信息UserDetials生成Token

  1. /**
  2.  * 根据用户信息生成Token
  3.  * @param userDetails
  4.  * @return
  5.  */
  6. public String generateToken(UserDetails userDetails){
  7.     //荷载
  8.     Map claim=new HashMap<>();
  9.     claim.put(CLAIM_KEY_USERNAME,userDetails.getUsername());
  10.     claim.put(CLAIM_KEY_CREATED,new Date());
  11.     return generateToken(claim);
  12. }
  13. /**
  14.  * 根据负载生成JWT Token
  15.  * @param claims
  16.  * @return
  17.  */
  18. private String generateToken(Map claims) {
  19.     return Jwts.builder()
  20.             .setClaims(claims)
  21.             .setExpiration(generateExpirationDate())//添加失效时间
  22.             .signWith(SignatureAlgorithm.HS512,secret)//添加密钥以及加密方式
  23.             .compact();
  24. }
  25. /**
  26.  * 生成Token失效时间  当前时间+配置的失效时间
  27.  * @return
  28.  */
  29. private Date generateExpirationDate() {
  30.     return new Date(System.currentTimeMillis()+expiration*1000);
  31. }
  • 根据Token生成用户名

  1. /**
  2.  * 根据Token生成用户名
  3.  * @param token
  4.  * @return
  5.  */
  6. public String getUsernameFormToken(String token){
  7.     String username;
  8.     //根据Token去拿荷载
  9.     try {
  10.         Claims claim=getClaimFromToken(token);
  11.         username=claim.getSubject();//获取用户名
  12.     } catch (Exception e) {
  13.         e.printStackTrace();
  14.         username=null;
  15.     }
  16.     return username;
  17. }
  18. /**
  19.  * 从Token中获取荷载
  20.  * @param token
  21.  * @return
  22.  */
  23. private Claims getClaimFromToken(String token) {
  24.     Claims claims=null;
  25.     try {
  26.         claims=Jwts.parser()
  27.                 .setSigningKey(secret)
  28.                 .parseClaimsJws(token)
  29.                 .getBody();
  30.     } catch (Exception e) {
  31.         e.printStackTrace();
  32.     }
  33.     return claims;
  34. }
  • 判断Token是否有效

  1. /**
  2.  * 判断Token是否有效
  3.  * Token是否过期
  4.  * Token中的username和UserDetails中的username是否一致
  5.  * @param token
  6.  * @param userDetails
  7.  * @return
  8.  */
  9. public boolean TokenIsValid(String token,UserDetails userDetails){
  10.     String username = getUsernameFormToken(token);
  11.     return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
  12. }
  13. /**
  14.  * 判断Token是否过期
  15.  * @param token
  16.  * @return
  17.  */
  18. private boolean isTokenExpired(String token) {
  19.     //获取Token的失效时间
  20.     Date expireDate=getExpiredDateFromToken(token);
  21.     //在当前时间之前,则失效
  22.     return expireDate.before(new Date());
  23. }
  24. /**
  25.  * 获取Token的失效时间
  26.  * @param token
  27.  * @return
  28.  */
  29. private Date getExpiredDateFromToken(String token) {
  30.     Claims claims = getClaimFromToken(token);
  31.     return claims.getExpiration();
  32. }
  • 判断Token是否可以被刷新

  1. /**
  2.  * 判断token是否可用被刷新
  3.  * 如果已经过期了,则可用被刷新,未过期,则不可用被刷新
  4.  * @param token
  5.  * @return
  6.  */
  7. public boolean canRefresh(String token){
  8.     return !isTokenExpired(token);
  9. }
  • 刷新Token,获取新的Token

  1. /**
  2.  * 刷新Token
  3.  * @param token
  4.  * @return
  5.  */
  6. public String refreshToken(String token){
  7.     Claims claims=getClaimFromToken(token);
  8.     claims.put(CLAIM_KEY_CREATED,new Date());
  9.     return generateToken(claims);
  10. }
<2>登录功能的实现

Controller层

  1. @ApiOperation(value = "登录之后返回token")
  2. @PostMapping("/login")
  3. //AdminLoginParam 自定义登录时传入的对象,包含账号,密码,验证码 
  4. public RespBean login(@RequestBody AdminLoginParam adminLoginParam, HttpServletRequest request){
  5.     return adminService.login(adminLoginParam.getUsername(),adminLoginParam.getPassword(),adminLoginParam.getCode(),request);
  6. }

Service层

  1. /**
  2.  * 登录之后返回token
  3.  * @param username
  4.  * @param password
  5.  * @param request
  6.  * @return
  7.  */
  8. @Override
  9. public RespBean login(String username, String password,String code, HttpServletRequest request) {
  10.     String captcha = (String)request.getSession().getAttribute("captcha");//验证码功能,后面提到
  11.     //验证码为空或匹配不上
  12.     if((code == null || code.length()==0) || !captcha.equalsIgnoreCase(code)){
  13.         return RespBean.error("验证码错误,请重新输入");
  14.     }
  15.     //通过username在数据库查出这个对象
  16.     //在SecurityConfig配置文件中,重写了loadUserByUsername方法,返回了userDetailsService Bean对象,使用我们自己的登录逻辑
  17.     UserDetails userDetails = userDetailsService.loadUserByUsername(username);
  18.     //如果userDetails为空或userDetails中的密码和传入的密码不相同
  19.     if (userDetails == null||!passwordEncoder.matches(password,userDetails.getPassword())){
  20.         return RespBean.error("用户名或密码不正确");
  21.     }
  22.     //判断账号是否可用
  23.     if(!userDetails.isEnabled()){
  24.         return RespBean.error("该账号已经被禁用,请联系管理员");
  25.     }
  26.     //更新登录用户对象,放入security全局中,密码不放
  27.     UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
  28.     SecurityContextHolder.getContext().setAuthentication(authenticationToken);
  29.     //生成token
  30.     String token = jwtTokenUtil.generateToken(userDetails);
  31.     Map tokenMap=new HashMap<>();
  32.     tokenMap.put("token",token);
  33.     tokenMap.put("tokenHead",tokenHead);//tokenHead,从配置文件yml中拿到的token的请求头 == Authorization
  34.     return RespBean.success("登陆成功",tokenMap);//将Token返回
  35. }
<3>退出登录

退出登录功能由前端实现,我们只需要返回一个成功信息即可

  1. @ApiOperation(value = "退出登录")
  2. @PostMapping("/logout")
  3. /**
  4.  * 退出登录
  5.  */
  6. public RespBean logout(){
  7.     return RespBean.success("注销成功");
  8. }
<4>获取当前登录用户信息

Controller层

  1. @ApiOperation(value = "获取当前登录用户的信息")
  2.     @GetMapping("/admin/info")
  3.     public Admin getAdminInfo(Principal principal){
  4.         //可通过principal对象获取当前登录对象
  5.         if(principal == null){
  6.             return null;
  7.         }
  8.         //当前用户的用户名
  9.         String username = principal.getName();
  10.         Admin admin= adminService.getAdminByUsername(username);
  11.         //不能返回前端用户密码,设置为空
  12.         admin.setPassword(null);
  13.         //将用户角色返回
  14.         admin.setRoles(adminService.getRoles(admin.getId()));
  15.         return admin;
  16.     }
<5>SpringSecurity的配置类SecurityConfig

覆盖SpringSecurity默认生成的账号密码,并让他走我们自定义的登录逻辑

  1. //让SpringSecurity走我们自己登陆的UserDetailsService逻辑
  2. //认证信息的管理 用户的存储 这里配置的用户信息会覆盖掉SpringSecurity默认生成的账号密码
  3. @Override
  4. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  5.     auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
  6. }
  7. //密码加解密
  8. @Bean
  9. public PasswordEncoder passwordEncoder(){
  10.     return new BCryptPasswordEncoder();
  11. }
  12. @Override
  13. @Bean  //注入到IOC中,在登录时使用到的userDetailsService就是这个Bean,loadUserByUsername方法是这里重写过的
  14. public UserDetailsService userDetailsService(){
  15.     return username->{
  16.         Admin admin=adminService.getAdminByUsername(username);
  17.         if(admin != null){
  18.             admin.setRoles(adminService.getRoles(admin.getId()));
  19.             return admin;
  20.         }
  21.         throw new UsernameNotFoundException("用户名或密码错误");
  22.     };
  23. }

登录功能中使用的userDetailsService对象由这里注入,重写loadUserByUsername方法实现自定义登录逻辑。

进行资源的拦截,权限设置,登录过滤器设置。

  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3.     //使用Jwt不需要csrf
  4.     http.csrf().disable()

联系站长

QQ:769220720

Copyright © SibooSoft All right reserved 津ICP备19011444号