SpringBoot2整合JWT实现单点登录

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">登 &nbsp; &nbsp;录</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地址了,错误需重新登录。


有疑问的大佬请留下评论哦!

猜你喜欢

转载自blog.csdn.net/qq_41085151/article/details/107386174