1.添加pom依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!--springboot版本 lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<!-- 用来序列化的 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.配置类,配置拦截请求
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(authInterceptor).addPathPatterns("/**");
registry.addInterceptor(authInterceptor).excludePathPatterns("/static/*");
registry.addInterceptor(authInterceptor).excludePathPatterns("/js/*");
registry.addInterceptor(authInterceptor).excludePathPatterns("/login");
WebMvcConfigurer.super.addInterceptors(registry);
}
@Bean
public RestTemplate RestTemplate(){
return new RestTemplate();
}
}
3.编写注解类控制需要登录的请求
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LoginRequired {
boolean isSuccess() default true;
}
4.需要存入浏览器Cookie中,Cookie工具类,和JWT工具类。
public class CookieUtil {
/***
* 获得cookie中的值,默认为主ip:www.itlinli.com
* @param request
* @param cookieName
* @param isDecoder
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
Cookie[] cookies = request.getCookies();
if (cookies == null || cookieName == null){
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equals(cookieName)) {
if (isDecoder) {//如果涉及中文
retValue = URLDecoder.decode(cookies[i].getValue(), "UTF-8");
} else {
retValue = cookies[i].getValue();
}
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return retValue;
}
/***
* 设置cookie的值
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage
* @param isEncode
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
try {
if (cookieValue == null) {
cookieValue = "";
} else if (isEncode) {
cookieValue = URLEncoder.encode(cookieValue, "utf-8");
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage >= 0)
cookie.setMaxAge(cookieMaxage);
//cookie.setSecure(true);
// cookie.setHttpOnly(true);
if (null != request)// 设置域名的cookie
cookie.setDomain(getDomainName(request));
// 在域名的根路径下保存
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/***
* 获得cookie的主域名,本系统为itlinli.com,保存时使用
* @param request
* @return
*/
private static final String getDomainName(HttpServletRequest request) {
String domainName = null;
String serverName = request.getRequestURL().toString();
if (serverName == null || serverName.equals("")) {
domainName = "";
} else {
serverName = serverName.toLowerCase();
serverName = serverName.substring(7);
final int end = serverName.indexOf("/");
serverName = serverName.substring(0, end);
final String[] domains = serverName.split("\\.");
int len = domains.length;
if (len > 3) {
// www.xxx.com.cn
domainName = domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
} else if (len <= 3 && len > 1) {
// xxx.com or xxx.cn
domainName = domains[len - 2] + "." + domains[len - 1];
} else {
domainName = serverName;
}
}
if (domainName != null && domainName.indexOf(":") > 0) {
String[] ary = domainName.split("\\:");
domainName = ary[0];
}
System.out.println("domainName = " + domainName);
return domainName;
}
/***
* 将cookie中的内容按照key删除
* @param request
* @param response
* @param cookieName
*/
public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String cookieName) {
setCookie(request, response, cookieName, null, 0, false);
}
}
public class JwtUtil {
/**
* token私钥
*/
private static final String TOKEN_SECRET = "itlinli";
public static String encode(String username,String userId){
//过期时间
// Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
//私钥及加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头信息
HashMap<String, Object> header = new HashMap<>(2);
header.put("typ", "JWT");
header.put("alg", "HS256");
//附带username和userID生成签名
return JWT.create().withHeader(header).withClaim("username",username)
.withClaim("userId",userId).sign(algorithm);
}
public static Map<String, String> decode(String token) throws Exception {
DecodedJWT jwt = null;
Map<String, Claim> map;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).build();
jwt = verifier.verify(token);
map=jwt.getClaims();
} catch (Exception e) {
throw new Exception("鉴权失败");
}
Map<String, String> resultMap = new HashMap<>(map.size());
map.forEach((k, v) -> resultMap.put(k, v.asString()));
return resultMap;
}
}
5.编写pojo,service, impl,我这里没有连数据库就直接写死账号和密码了。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Loginbean {
private Long userId;
private String username;
private String password;
}
public interface UserService {
Boolean login(Loginbean loginbean);
}
@Service
public class UserServiceImpl implements UserService {
@Override
public Boolean login(Loginbean loginbean) {
String username = loginbean.getUsername();
String password = loginbean.getPassword();
if ("zhangsan".equals(username)&&"123".equals(password)){
return true;
}else{
return false;
}
}
}
6.设置拦截器
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {
@Autowired
RestTemplate restTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod= (HandlerMethod) handler;
LoginRequired methodAnnotation = handlerMethod.getMethodAnnotation(LoginRequired.class);//获取请求中有没有带此注解类
if (methodAnnotation==null){
return true;
}else{
// 先获得用户cookie中关于用户的身份token
//token有四种情况
String token = "";
String oldToken = CookieUtil.getCookieValue(request, "oldToken", true);//登录过就获取浏览器的cookie值
if(StringUtils.isNotBlank(oldToken)){
token = oldToken;
}
String newToken = request.getParameter("newToken");//获取登录时传过来的token值
if(StringUtils.isNotBlank(newToken)){
token = newToken;
}
if(StringUtils.isNotBlank(token)){
// 验证用户的token是否正确
// 通过远程ws请求认证中心,验证token
String requestUrl = "http://127.0.0.1:8099/verify?token="+token;
String successJSON = restTemplate.getForObject(requestUrl, String.class);
HashMap<String,String> hashMap = new HashMap<>(); //把json转成map集合
HashMap hashMapJSON = JSON.parseObject(successJSON, hashMap.getClass());
if(hashMapJSON!=null&&hashMapJSON.get("success").equals("success")){//如果不为空并且是有传过来的success字符串
// 重新更新cookie的过期时间
CookieUtil.setCookie(request,response,"oldToken",token,1*60,true);
request.setAttribute("userId",hashMapJSON.get("userId"));//把拦截器的request存入数据转发到,请求的地址去。
request.setAttribute("username",hashMapJSON.get("username"));
}else{
if(methodAnnotation.isSuccess()){//不是同一个token用户返回页面
String ReturnUrl = request.getRequestURL().toString();
System.out.println("不是同一个token用户返回页面!");
response.sendRedirect("http://127.0.0.1:8099/index?ReturnUrl="+ReturnUrl);
// String ReturnUrl = request.getRequestURL().toString();
// response.sendRedirect("http://127.0.0.1:8090/index?ReturnUrl="+ReturnUrl);
/* response.setContentType("text/html;charset=UTF-8");
response.getWriter().print("不是同一个token用户返回页面!");*/
// 拦截验证
return false;
}
}
}else{
if(methodAnnotation.isSuccess()) {//判断注解是否是tuer
System.out.println("没有登录,没有token值,返回登录页面");
String ReturnUrl = request.getRequestURL().toString();//没有登录,没有token值,返回登录页面
response.sendRedirect("http://127.0.0.1:8099/index?ReturnUrl=" + ReturnUrl);
//String ReturnUrl = request.getRequestURL().toString();//没有登录,没有token值,返回登录页面
//response.sendRedirect("http://127.0.0.1:8090/index?ReturnUrl=" + ReturnUrl);
/*response.setContentType("text/html;charset=UTF-8");
response.getWriter().print("没有登录,没有token值,返回登录页面");*/
// 拦截验证
return false;
}
}
}
return true;
}
}
7.编写控制层
@Controller
public class loginController {
@Autowired
UserService userService;
@PostMapping("/login")
@ResponseBody
public String login(@RequestParam("username") String username, @RequestParam("password") String password,
HttpServletRequest request, HttpServletResponse response, ModelMap modelMap){
Loginbean loginbean = new Loginbean();
loginbean.setUsername(username);
loginbean.setPassword(password);
loginbean.setUserId(1043452L);
Boolean issuccess = userService.login(loginbean);
if (!issuccess) {
return "fail";//返回一个提示
} else { //登录成功
// 根据已经登录的用户信息和,服务器密钥,和其他盐值(根据系统算法)生成一个token
String key = "itlinlisso";
//String ip = request.getRemoteAddr();
//String ip = request.getHeader("x-forward-for");//nginx
//String token = JwtUtil.encode(key, map, ip);
String name = loginbean.getUsername();
Long userId = loginbean.getUserId();
String id = String.valueOf(userId);
String token = JwtUtil.encode(name,id);
// 将生成的token和登录用户信息保存在缓存中一分
//userService.addUserCache(token, umsMemberFromDb);
CookieUtil.setCookie(request,response,"oldToken",token,30*60,true);
//System.out.println(cartListCookieStr);
/* if (cartListCookieStr!=null) {
//发送消息队列同步购物车数据
userService.addUsercartDBredis(cartListCookieStr,umsMemberFromDb.getId());
CookieUtil.deleteCookie(request,response,cartListCookieStr);
}*/
return token;
}
}
@GetMapping("verify")
@ResponseBody
public String verify(String token) {
System.out.println("认证中心认证用户的token");
//String key = "itlinlisso";
//String ip = currentIp;
Map<String, String> map = null;//验证token是否是同一个。不是则返回null
Map<String, String> verifyReturn = new HashMap<>();
try {
map = JwtUtil.decode(token);
String userId = map.get("userId");
String username = map.get("username");
verifyReturn.put("success", "success"); //存入用户id和姓名
verifyReturn.put("userId", userId);
verifyReturn.put("username", username);
System.out.println(verifyReturn);
return JSON.toJSONString(verifyReturn);//转成json格式返回
} catch (Exception e) {
verifyReturn.put("success", "fail");
return JSON.toJSONString(verifyReturn);
}
}
@RequestMapping("index")
public String index(String ReturnUrl, ModelMap modelMap) {
System.out.println("认证中心首页");
modelMap.put("ReturnUrl", ReturnUrl);
return "index";
}
@RequestMapping("/mian")
@LoginRequired
public String mian(HttpServletRequest request,ModelMap modelMap){
String memberId = (String) request.getAttribute("userId");
String nickname = (String) request.getAttribute("username");
modelMap.put("memberId",memberId);
modelMap.put("nickname",nickname);
return "mian";
}
}
8.使用thymeleaf,配置文件中就设置端口号为8099的,设置thymeleaf配置随意。
在thymeleaf目录下创建index.html jquery在static/js目录
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
<script src="/js/jquery-3.1.1.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
用户名:<input type="text" name="username" id="username" ><br>
密码:<input type="text" name="password" id="password"><br>
<a href="javascript:submitLogin();" class="a">登 录</a>
<!--底部-->
<input type="hidden" id="ReturnUrl" th:value="${ReturnUrl}"/><br />
</body>
<script>
function submitLogin() {
var username = $("#username").val();
var password = $("#password").val();
$.post("login",{username:username,password:password},function(token){
if(token=="fail"){
alert("登录失败,用户名和密码错误");
}else{
alert(token);
window.location.href=$("#ReturnUrl").val()+"?newToken="+token;
}
});
}
</script>
</html>
9.设置mian.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>访问主页</title>
</head>
<body>
<h3>登录成功!</h3>
<input type="text" th:value="${nickname}">
<input type="text" th:value="${memberId}">
</body>
</html>
10.运行项目测试访问需要登录的http://localhost:8099/mian方法。浏览器中没有token消息,返回登录页面。使用不同的token也会需要重新登录。
11.验证账号信息成功的时候,会返回http://localhost:8099/mian?newToken=xxx 去请求这个地址,该请求有注解,请求的时候拦截器AuthInterceptor拦截到获取newToken值,然后验证是否正确,正确就可以继续返回mian地址了,错误需重新登录。