一、基础登录功能
1.Controller层
import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class LoginController {
@Autowired
private EmpService empService;
@PostMapping("/login")
public Result login(@RequestBody Emp emp){
log.info ( "登录用户名:{},密码:{}",emp );
Emp e =empService.login(emp);
return e!=null?Result.success ():Result.error ( "用户名或密码错误" );
}
}
2.Service层
@Override
public Emp login(Emp emp) {
return empMapper.getByusernameAndPassword(emp);
}
}
3.Mapper层
/**
* 根据用户名密码查询员工
* @param emp
* @return
*/
@Select ( "select * from emp where username=#{username} and password=#{password}")
Emp getByusernameAndPassword(Emp emp);
二、登录校验
1.会话技术
1.会话跟踪方案对比
import com.itheima.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* HttpSession演示
*/
@Slf4j
@RestController
public class SessionController {
//设置Cookie
@GetMapping("/c1")
public Result cookie1(HttpServletResponse response){
response.addCookie(new Cookie("login_username","itheima")); //设置Cookie/响应Cookie
return Result.success();
}
//获取Cookie
@GetMapping("/c2")
public Result cookie2(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if(cookie.getName().equals("login_username")){
System.out.println("login_username: "+cookie.getValue()); //输出name为login_username的cookie
}
}
return Result.success();
}
@GetMapping("/s1")
public Result session1(HttpSession session){
log.info("HttpSession-s1: {}", session.hashCode());
session.setAttribute("loginUser", "tom"); //往session中存储数据
return Result.success();
}
@GetMapping("/s2")
public Result session2(HttpServletRequest request){
HttpSession session = request.getSession();
log.info("HttpSession-s2: {}", session.hashCode());
Object loginUser = session.getAttribute("loginUser"); //从session中获取数据
log.info("loginUser: {}", loginUser);
return Result.success(loginUser);
}
}
2.JWT令牌技术
/**
* 测试Jwt令牌的生成
*/
@Test
public void testGenJwt(){
Map<String, Object> claims=new HashMap<> ();
claims.put ( "id",1 );
claims.put ( "name","TOM" );
String jwt = Jwts.builder ()
.signWith ( SignatureAlgorithm.HS256, "itheima" )//签名算法
.setClaims ( claims )//自定义内容(载荷)
.setExpiration ( new Date ( System.currentTimeMillis () ) )
// .setExpiration ( new Date ( System.currentTimeMillis () + 3600 * 1000 ) )//设置令牌的有效期为1个小时
.compact ();//返回一个jwt令牌
System.out.println (jwt);
}
1.生成JWT令牌
2.令牌解析
@Test
public void testPrasejwt(){
Claims claims = Jwts.parser ()
.setSigningKey ( "itheima" )
.parseClaimsJws ( "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVE9NIiwiaWQiOjEsImV4cCI6MTY5NzAwOTgwN30.9iduxbB4vpQLGs3anczG-spuQdT5f_3BR6aHYYHop3M" )
.getBody ();
System.out.println ( claims );
}
3.登陆后下发令牌
1.jwtUtils类
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private static String signKey = "itheima";
private static Long expire = 43200000L;
/**
* 生成JWT令牌
* @param claims JWT第二部分负载 payload 中存储的内容
* @return
*/
public static String generateJwt(Map<String, Object> claims){
String jwt = Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, signKey)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}
/**
* 解析JWT令牌
* @param jwt JWT令牌
* @return JWT第二部分负载 payload 中存储的内容
*/
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
2.Controller层
import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
import com.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
public class LoginController {
@Autowired
private EmpService empService;
@PostMapping("/login")
public Result login(@RequestBody Emp emp){
log.info ( "登录用户名:{},密码:{}",emp );
Emp e =empService.login(emp);
//登陆成功,生成令牌,下发令牌
if (e!=null){
Map<String, Object> claims=new HashMap<> ();
claims.put ( "id",e.getId () );
claims.put ( "name",e.getUsername () );
claims.put ( "username",e.getUsername () );
String jwt = JwtUtils.generateJwt ( claims );//jwt 包含了当前登录的员工信息
return Result.success (jwt);//下发jwt令牌
}
//登录失败,返回错误信息
return Result.error ( "用户名或密码错误" );
}
}
三、过滤器 -Filter
1.入门程序
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
@Override //初始化方法只调用一次
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println ("初始化方法被调用了");
}
@Override //拦截到请求之后调用
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
System.out.println ("拦截到了请求");
chain.doFilter ( servletRequest,servletResponse );//放行
}
@Override //销毁方法,只调用一次
public void destroy() {
System.out.println ("销毁方法被调用了");
}
}
2.Filter详解
1.Filter执行流程
2.过滤器的拦截路径
3.过滤器链
3.Filter完成登录校验
1.LoginCheckFilter
import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
//1.获取请求url
String url = req.getRequestURI ().toString ();
log.info ( "请求的url:{}",url );
//2.判断请求url中是否包含login,如果包含,说明是登陆操作,放行
if (url.contains ( "login" )){
log.info ("登陆操作,放行...");
filterChain.doFilter (servletRequest,servletResponse );
return;
}
//3.获取请求头的令牌(token)
String jwt = req.getHeader ( "token" );
//4.判断令牌是否存在,如果不存在,返错误结果(未登录)
if (!StringUtils.hasLength ( jwt )){
log.info ( "请求头token为空,返回未登录的信息" );
Result error = Result.error ( "NOT_LOGIN" );
//手动转换Josn数据
String notLogin = JSONObject.toJSONString ( error );
resp.getWriter ().write ( notLogin );
return;
}
//5.解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parseJWT ( jwt );
} catch (Exception e) {
e.printStackTrace ();
log.info ( "解析令牌失败,返回未登录错误信息" );
Result error = Result.error ( "NOT_LOGIN" );
//手动转换Josn数据
String notLogin = JSONObject.toJSONString ( error );
resp.getWriter ().write ( notLogin );
return;
}
//6.放行
log.info ( "令牌合法,放行" );
filterChain.doFilter ( servletRequest,servletResponse );
}
}
2.手动将字符串转换为JSON格式数据
//手动转换Josn数据
String notLogin = JSONObject.toJSONString ( error );
resp.getWriter ().write ( notLogin );
3.引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
四、拦截器
1.拦截器的基本使用
1.定义拦截器
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override//目标资源方法运行前运行,返回true 放行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println ("preHandle...");
return true;
}
@Override//目标资源方法运行后运行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println ("postHandle.....");
}
@Override//视图渲染完毕后,最后运行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println ("afterCompletion...");
}
}
2.配置拦截器
import com.itheima.interceptor.LoginCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration//配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor ( loginCheckInterceptor ).addPathPatterns ( "/**" );
}
}
2.拦截路径,执行流程
3.拦截器完成登录校验
import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
//@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
//1.获取请求url
String url = req.getRequestURI ().toString ();
log.info ( "请求的url:{}",url );
//2.判断请求url中是否包含login,如果包含,说明是登陆操作,放行
if (url.contains ( "login" )){
log.info ("登陆操作,放行...");
filterChain.doFilter (servletRequest,servletResponse );
return;
}
//3.获取请求头的令牌(token)
String jwt = req.getHeader ( "token" );
//4.判断令牌是否存在,如果不存在,返错误结果(未登录)
if (!StringUtils.hasLength ( jwt )){
log.info ( "请求头token为空,返回未登录的信息" );
Result error = Result.error ( "NOT_LOGIN" );
//手动转换Josn数据
String notLogin = JSONObject.toJSONString ( error );
resp.getWriter ().write ( notLogin );
return;
}
//5.解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parseJWT ( jwt );
} catch (Exception e) {
e.printStackTrace ();
log.info ( "解析令牌失败,返回未登录错误信息" );
Result error = Result.error ( "NOT_LOGIN" );
//手动转换Josn数据
String notLogin = JSONObject.toJSONString ( error );
resp.getWriter ().write ( notLogin );
return;
}
//6.放行
log.info ( "令牌合法,放行" );
filterChain.doFilter ( servletRequest,servletResponse );
}
}
五、异常处理
import com.itheima.pojo.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)//捕获异常的类型
public Result ex(Exception exception){
return Result.error ( "对不起操作失败,请联系管理员" );
}
}