005-云E办_登录功能

  • 一、依赖、配置、工具类、公共返回对象
    • 1、依赖、配置
    • 2、jwt令牌的工具类
    • 3、公共返回对象
      • pojo/respBean (公共返回对象)
  • 二、登录流程:
    • 1、登录之后返回的token
      • 1.pojo/Admin下实现UserDetails:
      • 3.pojo/adminLoginParam
      • 2.controller/LoginController
      • 4.service/IAdminService
    • 2、退出功能和获取当前登录用户信息
      • 退出功能和获取当前登录用户信息
      • 1.LoginController
      • 2.service/AdminService及实现类:
    • 3、配置Security登录的授权过滤器
      • 1.Security自定义返回结果:

一、依赖、配置、工具类、公共返回对象

采用SpringSecurity安全框架以及JWT令牌实现登录功能。

1、依赖、配置

引入依赖:

<!--security 依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--JWT 依赖-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version>
</dependency>

加入yaml配置:
secret:jwt加密以及解密要用到的秘钥

jwt:# JWT存储的请求头tokenHeader: Authorization# JWT 加解密使用的密钥secret: yeb-secret# JWT的超期限时间(60*60*24)expiration: 604800# JWT 负载中拿到开头tokenHead: Bearer

2、jwt令牌的工具类

方便后边跟jwt的功能而使用。
创建config包,用来放项目要用的配置。
创建JwtTokenUtil

1、根据用户信息生成token
2、从token中获取用户名(获取用户名先 必须先获取荷载)
3、验证token是否失效
4、判断token是否可以刷新
5、刷新token
/*** JwtToken工具类*/
@Component
public class JwtTokenUtil {//荷载 用户名的keyprivate static final String CLAIM_KEY_USERNAME="sub";//jwt的创建时间private static final String CLAIM_KEY_CREATED="created";//jwt的秘钥以及失效时间,通过刚刚的配置目录去拿。通过value注解@Value("${jwt.secret}")private String secret;//失效时间:@Value("${jwt.expiration}")private Long expiration;/*1.根据用户名生成token2.根据token拿到用户名2.判断token是否失效3.判断token是否能被刷新4.刷新token*//*** 1根据用户名信息生成token*/public String generateToken(UserDetails userDetails){Map<String,Object> claims = new HashMap<>();claims.put(CLAIM_KEY_USERNAME,userDetails.getUsername());claims.put(CLAIM_KEY_CREATED,new Date());//将荷载存入return generateToken(claims);}/*** 2从token中获取登录用户名* @param* @param token* @return** 按键盘上 CTRL +ALT + t  快捷键*/public String getUserNameFromToken(String token){String username=null;try {//先获取荷载,因为登录用户名是放在荷载中的Claims claims = getClaimsFromToken(token);//拿到荷载,通过荷载拿到登录用户名username = claims.getSubject();} catch (Exception e) {username=null;}return username;}/*** 2.1从token中获取荷载* @param token* @return*/private Claims getClaimsFromToken(String token) {Claims claims=null;try {claims = Jwts.parser()//前面放进去(秘钥).setSigningKey(secret)//转荷载.parseClaimsJws(token).getBody();} catch (Exception e) {e.printStackTrace();}return claims;}/*** 3判断token是否有效:*  1.是否过期*  2.token荷载的用户名和userDetails的用户名是否一致。* @return*/public boolean validateToken(String token, UserDetails userDetails){//获取用户名String username = getUserNameFromToken(token);return username.equals(userDetails.getUsername()) && !isTokenExpiration(token);}/*** 4判断token是否能被刷新:* isTokenExpiration=true*  说明以及过期了,则false. 取反则能刷新了* @param token* @return*/public boolean canRefresh(String token){//如果过期了就可以被刷新return !isTokenExpiration(token);}/*** 刷新token* @param token* @return*/public String RefreshToken(String token){//刷新就是把过期时间更改一下:Claims claims = getClaimsFromToken(token);claims.put(CLAIM_KEY_CREATED,new Date());return generateToken(claims);}/*** 3.1判断token是否失效:* @param token* @return*/private boolean isTokenExpiration(String token) {//先获取失效时间:Date exprireDate = getExpiredDateFromToken(token);//判断:失效时间:是否在当前时间的前面return exprireDate.before(new Date());}/*** 3.2从token中获取失效时间:* @param token* @return*/private Date getExpiredDateFromToken(String token) {//从token中获取荷载,因为过期时间在荷载里面Claims claims = getClaimsFromToken(token);//过期时间:return claims.getExpiration();}/*** 1根据荷载生成Jwt token* 参数是荷载。*/private String generateToken(Map<String,Object> claims){return Jwts.builder()//传入荷载.setClaims(claims)//设定过期时间.setExpiration(generateExpirationDate())//签名算法,秘钥.signWith(SignatureAlgorithm.ES512,secret).compact();}/*** 1生成token失效时间* @return*/private Date generateExpirationDate() {//失效时间:当前时间+过期时间return new Date(System.currentTimeMillis()+expiration*1000);}}

3、公共返回对象

流程:
前端转过来用户名和密码,后端先去校验用户名和密码,如果错误让用户重新输入。如果正确则返回一个jwt令牌,传给前端。前端拿到jwt令牌后放在请求头里面,后面的任何请求都会携带jwt令牌,后端会有拦截器对jwt令牌进行验证,验证通过后,才能访问对应的接口。如果jwt验证不通过,那说明失效了、或者用户名有问题。

后端第一步根据用户名和密码登录后,会返给前端一个令牌。那后面整个项目都会有一些公用的返回东西,那么写一个公共返回对象。

pojo/respBean (公共返回对象)

//lombok:无参构造、全参构造
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespBean {//状态码private long code;//提示信息private String message;//可能返回一下对象private Object obj;//可以定义一些返回的东西了:/*** 成功返回结果* @param message* @return*/public static RespBean success(String message){return new RespBean(200,message,null);}/*** 成功返回结果* @param message* @param obj* @return*/public static RespBean success(String message,Object obj){return new RespBean(200,message,obj);}/*** 失败返回的结果* @param message* @return*/public static RespBean error(String message){return new RespBean(500,message,null);}/*** 失败返回结果:* @param message* @param obj* @return*/public static RespBean error(String message,Object obj){return new RespBean(500,message,obj);}
}

二、登录流程:

1、登录之后返回的token

登录的流程:
前端输入账号和密码传给后端,后端去判断用户和密码是否正确。正确的话就会给生成一个jwt令牌返回给前端。如果不正确,就让用户重新输入。
前端拿到我们生成的jwt令牌之后,就会把令牌放到请求头里面,后面的每一次请求都会携带jwt令牌。我们后端会写一个jwt令牌相关的拦截器,每次请求页面之前都会去拦截器判断jwt令牌是否存在、是否合法有效。如果不存在、合法无效的jwt令牌的话,就会拦截掉,不让访问去接口。如果存在合法有效的jwt令牌的话,就运行访问其他的接口。

判断前端传来的用户名和密码,返回jwt令牌
前端传用户名和密码,后端判断用户名和密码是否正确,正确的话生成jwt令牌

1.pojo/Admin下实现UserDetails:

public class Admin implements Serializable , UserDetails {/*** 已经是SpringSecurity框架了* 真正登录的方法就是UserDetails的Username* 登陆成功就是details* @return*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return enabled;}
}

3.pojo/adminLoginParam

/*** 用户登录实体类:* 专门用来专递前端传给我们的用户名和密码* 登录只需要给后端传用户名和密码*/
//lombok:
@Data
@EqualsAndHashCode(callSuper = false)
//具体的作用是开启链式编程,让我们写代码更加方便。
@Accessors(chain = true)
//接口文档:swagger:
@ApiModel(value = "AdminLogin对象",description = "")
public class adminLoginParam {@ApiModelProperty(value = "用户名",required=true)private String username;@ApiModelProperty(value = "密码",required = true)private String password;
}

2.controller/LoginController

@Api(tags = "LoginController")
@RestController
public class LoginController {//注入serviceprivate IAdminService adminService;//用swagger注解代表注释了@ApiOperation(value = "登录之后返回token")@PostMapping("/login")public RespBean login(AdminLoginParam adminLoginParam, HttpServletRequest request){//service层login登录方法return adminService.login(adminLoginParam.getUsername(),adminLoginParam.getPassword(),request);}
}

4.service/IAdminService

    /*** 登录之后,返回token* @param username* @param password* @param request* @return*/RespBean login(String username, String password, HttpServletRequest request);
----实现类:
/*** 登录之后返回token* @param username* @param password* @param request* @return*/@Overridepublic RespBean login(String username, String password, HttpServletRequest request) {//security主要是通过:UserDetailsService里面的username来实现登录的//将浏览器传过来的username,放进去。 返回的是userDetails用户详细信息(账号、密码、权限等等)UserDetails userDetails = userDetailsService.loadUserByUsername(username);//判断传过来的username是否为空 或者 (浏览器输入的和数据库密码不一致) 则密码或者用户名是错的if(userDetails==null ||passwordEncoder.matches(password,userDetails.getPassword())){return RespBean.error("用户名或者密码不正确");}//判断是否禁用if(!userDetails.isEnabled()){return RespBean.error("账号被禁用");}/*** 更新security登录用户对象* 参数:userDetails,凭证密码null,权限列表** security的全局里面*/UsernamePasswordAuthenticationToken authenticationToken= new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());//上下文持有人SecurityContextHolder.getContext().setAuthentication(authenticationToken);/*** 生成token返回给前端* 如果以上都没有进入判断,说明用户和密码是正确的:就可以拿到jwt令牌了:* 根据用户信息生成令牌*/String token = jwtTokenUtil.generateToken(userDetails);//有了token,就用map返回:Map<String,String> tokenMap=new HashMap<>();//将token返回去tokenMap.put("token",token);//头部信息也返回去前端,让他放在请求头里面tokenMap.put("tokenHead",tokenHead);return RespBean.success("登陆成功",tokenMap);}

2、退出功能和获取当前登录用户信息

退出功能和获取当前登录用户信息

退出功能比较简单,前端来处理退出功能。后端只要返回一个成功的接口即可。
主要是前端获取一个状态码:200
前端在请求头里面把token删除,再去调用接口就会被拦截器拦截

1.LoginController

     /*** 获取当前登录的用户信息*/@ApiOperation(value = "获取当前用户登录的信息")@GetMapping("/admin/info")public Admin getAdminInfo(Principal principal){if(null==principal){return null;}String username = principal.getName();//根据用户名获取完全的用户对象Admin admin = adminService.getAdminByUserName(username);//但是用户的密码不会返回给浏览器admin.setPassword(null);return admin;}/*** 退出登录* @return*/@ApiOperation(value = "退出登录")@PostMapping("/logout")public RespBean logout(){return RespBean.success("注销成功");}

2.service/AdminService及实现类:

IAdminService接口

public interface IAdminService extends IService<Admin> {/*** 根据用户名获取用户:* @param username* @return*/Admin getAdminByUserName(String username);
}
-----实现类:/*** 根据用户名获取对象* @param username* @return* 自动注入Mapper 因为去数据库查询了:*/@Overridepublic Admin getAdminByUserName(String username) {/** 查询一个(泛型是admin。equals(提示:表的字段"username":username));* 1.用户名去匹配* 2.账户是否禁用*/return adminMapper.selectOne(new QueryWrapper<Admin>().eq("username",username).eq("enabled",true));}

3、配置Security登录的授权过滤器

config/security/sercurityConfig

package com.xxxx.server.config.security;import com.xxxx.server.pojo.Admin;
import com.xxxx.server.service.IAdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
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.authentication.UsernamePasswordAuthenticationFilter;/*** SpringSecurity主体的登录逻辑:* UserDetails里面的loud把user..实现了*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate IAdminService adminService;//由于我们自己重写了UserDetailsService,希望调用的时候调用我们自己重写的方法@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/login","logout").permitAll().anyRequest().authenticated().and().headers().cacheControl();//添加jwt,登录过滤器http.addFilterBefore(jwtAuthencationTokenFilter(), UsernamePasswordAuthenticationFilter.class);//添加自定义未授权和未登录结果返回http.exceptionHandling().accessDeniedHandler().authenticationEntryPoint();}//重写了userDetailsService@Override@Beanpublic UserDetailsService userDetailsService(){return username ->{Admin admin = adminService.getAdminByUserName(username);if(null!=admin){return admin;}return null;};}@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}//bean注解暴露出来@Beanpublic JwtAuthencationTokenFilter jwtAuthencationTokenFilter(){return new JwtAuthencationTokenFilter();}
}

JwtAuthencationTokenFilter

/*** JWT 登录授权过滤器** 前置拦截*/public class JwtAuthencationTokenFilter extends OncePerRequestFilter {@Value("${jwt.tokenHender}")private String tokenHeader;@Value("${jwt.tokenHead}")private String tokenHead;@Autowiredprivate JwtTokenUtil jwtTokenUtil;//d登录需要userDetailsService@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {//Header是前端传给我们.//要验证的头:String authHeader = request.getHeader(tokenHeader);//存在token,如果不存在或者开头不是tokenHeadif(null!=authHeader &&authHeader.startsWith(tokenHead)){String authToken = authHeader.substring(tokenHead.length());//从token中获取用户名String userName = jwtTokenUtil.getUserNameFromToken(authToken);//token存在,用户名未登录if(null!=userName&& null== SecurityContextHolder.getContext().getAuthentication()){//登录了UserDetails userDetails = userDetailsService.loadUserByUsername(userName);//重新放到用户对象当中:返回是booleanif(jwtTokenUtil.validateToken(AuthToken,userDetails)){UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authenticationToken);}}}//放行filterChain.doFilter(request,httpServletResponse);}
}

1.Security自定义返回结果:

RestAuthorizationEntryPoint

/*** 当未登录或者token失效访问接口时,自定义的返回结果*/
@Component
public class RestAuthorizationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {//设置编码格式:ut-f8httpServletResponse.setCharacterEncoding("UTF-8");//Json格式httpServletResponse.setContentType("application/json");PrintWriter out = httpServletResponse.getWriter();RespBean bean = RespBean.error("未登录,请登录");bean.setCode(401);out.flush();out.close();}
}

RestfulAccessDeniedHandler

/*** 当权限接口没有权限时,自定义返回结果:**/
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {httpServletResponse.setCharacterEncoding("UTF-8");httpServletResponse.setContentType("application/json");PrintWriter out = httpServletResponse.getWriter();RespBean bean = RespBean.error("权限不足,请联系管理员");bean.setCode(403);out.write(new ObjectMapper().writeValueAsString(bean));out.flush();out.close();}
}

006-云E办_登录功能相关推荐

  1. c 语言自动登录2016qq,快速集成腾讯云通讯IMSDK 注册登录功能

    云通讯简介 腾讯是国内最大也是最早的即时通信开发商,QQ和微信已经成为每个互联网用户必不可少的应用.现在,腾讯将高并发.高可靠的即时通信能力进行开放,开发者可以很容易的根据腾讯提供的SDK将即时通信功 ...

  2. 小程序(四):微信登录功能的实现+云开发数据库

    目录 好文推荐: 完整代码gitee仓库查看:https://gitee.com/CMD-UROOT/xzyy.git 一.微信登录 1.点击按钮(或其他),获取用户信息(昵称.头像等). 2.通过微 ...

  3. SpringBoot集成腾讯云短信实现注册/登录功能

    前言 笔记参考 Duktig丶 JavaSDK地址:短信 Java SDK - SDK 文档 - 文档中心 - 腾讯云 1.导入依赖 <dependency><groupId> ...

  4. 探花交友_第1章_项目介绍以及实现登录功能_第2节_项目介绍

    探花交友_第1章_项目介绍以及实现登录功能_第2节_项目介绍 文章目录 探花交友_第1章_项目介绍以及实现登录功能_第2节_项目介绍 2.项目介绍 2.1.项目背景 2.2.市场分析 2.3.目标用户 ...

  5. 云e办学习笔记(七)登录返回token(未完全)

    前言 本系列博客基于B站的云e办管理系统,前端和后端我都自己敲了一遍,这里做一个学习记录.云e办的原始视频链接如下:https://www.bilibili.com/video/BV1Ai4y1P7T ...

  6. ios设计登录功能_亲爱的产品设计师,这是iOS 14的新功能

    ios设计登录功能 On June 22, 2020 Apple previewed iOS 14 for the first time. As always there are quite some ...

  7. 云函数查询_速览 | 京东云5月重要产品与功能更新

    新增产品 高可用组 正式发布 产品概述: 高可用组是京东云提供的云主机逻辑集合,高可用组内的云主机分散部署在相互隔离的物理资源上,当出现硬件故障或定时维护时只会影响部分云主机,客户业务仍为可用状态,本 ...

  8. 襄阳教育云平台实名认证_襄阳教育云平台登录入口下载|襄阳教育云平台手机版登录入口 V3.8.7-清风安卓软件网...

    襄阳教育云平台登录入口:是一款非常优质的教育平台,用户在这里可以掌握到很多最新的教育动态,平台中也会将当地教育部推行的政策公布出来,方便家长来了解,而对于学生来说也能在平台中获取跟多专业的资源且都是和 ...

  9. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

最新文章

  1. 企业项目学习准备阶段——Rhel6.5版本无图形虚拟机封装过程及相关配置
  2. 一文总结微软研究院Transformer霸榜模型三部曲!
  3. JDK13 GA发布:5大特性解读
  4. 超美的文件夹图标,右键秒改,实用方便适合文件夹分类
  5. php换设备登录逻辑,登录和退出登录的操作逻辑
  6. 软件测试测试用例编写 不超过7步骤_教你快速编写一个合格的测试用例!
  7. 从毕业生当中看人与人的差距
  8. 电脑格式化的危害_防止硬盘被格式化的六大方法
  9. 编程练习赛11B 物品价值(装压dp)
  10. XML 在SQLServer中的使用
  11. Android上的抓包工具 Packet Capture
  12. cad会员共享_CAD迷你画图共享版下载
  13. 计算机二进制乘法运算(原码,补码)
  14. 【python--爬虫】守望先锋英雄介绍视频爬虫
  15. java基于springboot校园音乐点歌网站平台ssm
  16. 百度,你出来解释下什么是文本相似?
  17. laravel发送邮件
  18. 微信小程序+云开发项目实战:商品类小程序(化妆品门店)
  19. 计算机英语写一封邮件给汤姆作文,英语作文带翻译写一封电子邮件
  20. 二.某龙端游中LUA的分析和调用

热门文章

  1. Java实现图形化计算器(支持括号、开平方)
  2. 2022手机号码JS正则表达式验证实例代码
  3. 把多余的内存空间虚拟成硬盘空间,享受飞一般的感觉!
  4. 这位新世界首富,到底有多强...
  5. hive over窗口函数使用
  6. 【可同步账单、预算和账户信息财务软件】Money Pro for Mac 2.1
  7. HTTP请求头、响应头详解
  8. [附源码]计算机毕业设计Python+uniapp家庭理财产品小程序10341(程序+lw+远程部署)
  9. 毕业设计模型可以用别人的吗
  10. oppor11可以按Android吗,oppo r11怎么样?oppor11全面深度评测图解