在线办公系统
目录
- 在线办公系统
1.开发环境的搭建及项目介绍
本项目目的是实现中小型企业的在线办公系统,云E办在线办公系统是一个用来管理日常的办公事务的一个系统
使用SpringSecurity做安全认证及权限管理,Redis做缓存,RabbitMq做邮件的发送,使用EasyPOI实现对员工数据的导入和导出,使用WebSocket做在线聊天
使用验证码登录
页面展示:
-
添加依赖
-
使用MyBatis的AutoGenerator自动生成mapper,service,Controller
2.登录模块及配置框架搭建
<1>Jwt工具类及对Token的处理
1.1根据用户信息生成Token
-
定义JWT负载中用户名的Key以及创建时间的Key
//用户名的key
private static final String CLAIM_KEY_USERNAME=“sub”;
//签名的时间
private static final String CLAIM_KEY_CREATED=“created”; -
从配置文件中拿到Jwt的密钥和失效时间
/**
- @Value的值有两类:
- ① ${ property : default_value }
- ② #{ obj.property? :default_value }
- 第一个注入的是外部配置文件对应的property,第二个则是SpEL表达式对应的内容。 那个
- default_value,就是前面的值为空时的默认值。注意二者的不同,#{}里面那个obj代表对象。
*/
//JWT密钥
@Value(“${jwt.secret}”)
private String secret;
//JWT失效时间
@Value(“${jwt.expiration}”)
private Long expiration; -
根据用户信息UserDetials生成Token
/**
- 根据用户信息生成Token
- @param userDetails
- @return
*/
public String generateToken(UserDetails userDetails){
//荷载
Map<String,Object> claim=new HashMap<>();
claim.put(CLAIM_KEY_USERNAME,userDetails.getUsername());
claim.put(CLAIM_KEY_CREATED,new Date());
return generateToken(claim);
}
/**
- 根据负载生成JWT Token
- @param claims
- @return
*/
private String generateToken(Map<String,Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())//添加失效时间
.signWith(SignatureAlgorithm.HS512,secret)//添加密钥以及加密方式
.compact();
}
/**
- 生成Token失效时间 当前时间+配置的失效时间
- @return
/
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis()+expiration1000);
}
1.2根据Token生成用户名
/**
* 根据Token生成用户名
* @param token
* @return
*/
public String getUsernameFormToken(String token){
String username;
//根据Token去拿荷载
try {
Claims claim=getClaimFromToken(token);
username=claim.getSubject();//获取用户名
} catch (Exception e) {
e.printStackTrace();
username=null;
}
return username;
}
/**
* 从Token中获取荷载
* @param token
* @return
*/
private Claims getClaimFromToken(String token) {
Claims claims=null;
try {
claims=Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
e.printStackTrace();
}
return claims;
}
1.3判断Token是否有效
/**
* 判断Token是否有效
* Token是否过期
* Token中的username和UserDetails中的username是否一致
* @param token
* @param userDetails
* @return
*/
public boolean TokenIsValid(String token,UserDetails userDetails){
String username = getUsernameFormToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
/**
* 判断Token是否过期
* @param token
* @return
*/
private boolean isTokenExpired(String token) {
//获取Token的失效时间
Date expireDate=getExpiredDateFromToken(token);
//在当前时间之前,则失效
return expireDate.before(new Date());
}
/**
* 获取Token的失效时间
* @param token
* @return
*/
private Date getExpiredDateFromToken(String token) {
Claims claims = getClaimFromToken(token);
return claims.getExpiration();
}
1.4判断Token是否可以被刷新
/**
* 判断token是否可用被刷新
* 如果已经过期了,则可用被刷新,未过期,则不可用被刷新
* @param token
* @return
*/
public boolean canRefresh(String token){
return !isTokenExpired(token);
}
1.5刷新Token,获取新的Token
/**
* 刷新Token
* @param token
* @return
*/
public String refreshToken(String token){
Claims claims=getClaimFromToken(token);
claims.put(CLAIM_KEY_CREATED,new Date());
return generateToken(claims);
}
<2>登录功能的实现
-
Controller层
@ApiOperation(value = "登录之后返回token") @PostMapping("/login") //AdminLoginParam 自定义登录时传入的对象,包含账号,密码,验证码 public RespBean login(@RequestBody AdminLoginParam adminLoginParam, HttpServletRequest request){ return adminService.login(adminLoginParam.getUsername(),adminLoginParam.getPassword(),adminLoginParam.getCode(),request); }
-
Service层
/** * 登录之后返回token * @param username * @param password * @param request * @return */ @Override public RespBean login(String username, String password,String code, HttpServletRequest request) { String captcha = (String)request.getSession().getAttribute("captcha");//验证码功能,后面提到 //验证码为空或匹配不上 if((code == null || code.length()==0) || !captcha.equalsIgnoreCase(code)){ return RespBean.error("验证码错误,请重新输入"); } //通过username在数据库查出这个对象 //在SecurityConfig配置文件中,重写了loadUserByUsername方法,返回了userDetailsService Bean对象,使用我们自己的登录逻辑 UserDetails userDetails = userDetailsService.loadUserByUsername(username); //如果userDetails为空或userDetails中的密码和传入的密码不相同 if (userDetails == null||!passwordEncoder.matches(password,userDetails.getPassword())){ return RespBean.error("用户名或密码不正确"); } //判断账号是否可用 if(!userDetails.isEnabled()){ return RespBean.error("该账号已经被禁用,请联系管理员"); } //更新登录用户对象,放入security全局中,密码不放 UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authenticationToken); //生成token String token = jwtTokenUtil.generateToken(userDetails); Map<String,String> tokenMap=new HashMap<>(); tokenMap.put("token",token); tokenMap.put("tokenHead",tokenHead);//tokenHead,从配置文件yml中拿到的token的请求头 == Authorization return RespBean.success("登陆成功",tokenMap);//将Token返回 }
<3>退出登录
退出登录功能由前端实现,我们只需要返回一个成功信息即可
@ApiOperation(value = "退出登录")
@PostMapping("/logout")
/**
* 退出登录
*/
public RespBean logout(){
return RespBean.success("注销成功");
}
<4>获取当前登录用户信息
-
Controller层
</@ApiOperation(value = "获取当前登录用户的信息") @GetMapping("/admin/info") public Admin getAdminInfo(Principal principal){ //可通过principal对象获取当前登录对象 if(principal == null){ return null; } //当前用户的用户名 String username = principal.getName(); Admin admin= adminService.getAdminByUsername(username); //不能返回前端用户密码,设置为空 admin.setPassword(null); //将用户角色返回 admin.setRoles(adminService.getRoles(admin.getId())); return admin; }