点击关注公众号,回复“1024”获取2TB学习资源!
1.开发环境的搭建及项目介绍
本项目目的是实现中小型企业的在线办公系统,云E办在线办公系统是一个用来管理日常的办公事务的一个系统。
使用SpringSecurity做安全认证及权限管理,Redis做缓存,RabbitMq做邮件的发送,使用EasyPOI实现对员工数据的导入和导出,使用WebSocket做在线聊天。
使用验证码登录

页面展示:

添加依赖
使用MyBatis的AutoGenerator自动生成mapper,service,Controller
2.登录模块及配置框架搭建
<1>Jwt工具类及对Token的处理
根据用户信息生成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
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
claims) { - return Jwts.builder()
- .setClaims(claims)
- .setExpiration(generateExpirationDate())//添加失效时间
- .signWith(SignatureAlgorithm.HS512,secret)//添加密钥以及加密方式
- .compact();
- }
-
- /**
- * 生成Token失效时间 当前时间+配置的失效时间
- * @return
- */
- private Date generateExpirationDate() {
- return new Date(System.currentTimeMillis()+expiration*1000);
- }
根据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;
- }
判断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();
- }
判断Token是否可以被刷新
- /**
- * 判断token是否可用被刷新
- * 如果已经过期了,则可用被刷新,未过期,则不可用被刷新
- * @param token
- * @return
- */
- public boolean canRefresh(String token){
- return !isTokenExpired(token);
- }
刷新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
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;
- }
<5>SpringSecurity的配置类SecurityConfig
覆盖SpringSecurity默认生成的账号密码,并让他走我们自定义的登录逻辑
- //让SpringSecurity走我们自己登陆的UserDetailsService逻辑
-
- //认证信息的管理 用户的存储 这里配置的用户信息会覆盖掉SpringSecurity默认生成的账号密码
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
- }
- //密码加解密
- @Bean
- public PasswordEncoder passwordEncoder(){
- return new BCryptPasswordEncoder();
- }
- @Override
- @Bean //注入到IOC中,在登录时使用到的userDetailsService就是这个Bean,loadUserByUsername方法是这里重写过的
- public UserDetailsService userDetailsService(){
- return username->{
- Admin admin=adminService.getAdminByUsername(username);
- if(admin != null){
- admin.setRoles(adminService.getRoles(admin.getId()));
- return admin;
- }
- throw new UsernameNotFoundException("用户名或密码错误");
- };
- }
登录功能中使用的userDetailsService对象由这里注入,重写loadUserByUsername方法实现自定义登录逻辑。
进行资源的拦截,权限设置,登录过滤器设置。
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- //使用Jwt不需要csrf
- http.csrf().disable()
-
原文:https://blog.csdn.net/mingongge/article/details/122571910
ERP
联系站长
QQ:769220720