赞
踩
这篇博客是在 Spring boot 入门教程-全局异常处理及日志输出 的基础上完成的。
我们在做项目时比如商城项目,有的页面的打开是需要登陆的而有的页面则不需要,所以这里就需要一种验证是否登录,或者登录是否过期,这里说一种token令牌+拦截器的方式。
生成token 使用JWT。
1.引入
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt</artifactId>
- <version>0.9.0</version>
- </dependency>
2.生成token令牌的工具类
- public class TokenUtils {
-
- /**
- * 签名秘钥
- */
- public static final String SECRET = "admin";
-
- /**
- * 生成token
- *
- * @param id 一般传入userName
- * @return
- */
- public static String createJwtToken(String id) {
- String issuer = "www.xxxx.com";
- String subject = "xxxx@126.com";
- long ttlMillis = 3600000;
- return createJwtToken(id, issuer, subject, ttlMillis);
- }
-
- /**
- * 生成Token
- *
- * @param id 编号
- * @param issuer 该JWT的签发者,是否使用是可选的
- * @param subject 该JWT所面向的用户,是否使用是可选的;
- * @param ttlMillis 签发时间 (有效时间,过期会报错)
- * @return token String
- */
- public static String createJwtToken(String id, String issuer, String subject, long ttlMillis) {
-
- // 签名算法 ,将对token进行签名
- SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
-
- // 生成签发时间
- long nowMillis = System.currentTimeMillis();
- Date now = new Date(nowMillis);
-
- // 通过秘钥签名JWT
- byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET);
- Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
-
- // Let's set the JWT Claims
- JwtBuilder builder = Jwts.builder().setId(id)
- .setIssuedAt(now)
- .setSubject(subject)
- .setIssuer(issuer)
- .signWith(signatureAlgorithm, signingKey);
-
- // if it has been specified, let's add the expiration
- if (ttlMillis >= 0) {
- long expMillis = nowMillis + ttlMillis;
- Date exp = new Date(expMillis);
- builder.setExpiration(exp);
- }
-
- // Builds the JWT and serializes it to a compact, URL-safe string
- return builder.compact();
-
- }
-
- // Sample method to validate and read the JWT
- public static Claims parseJWT(String jwt) {
- // This line will throw an exception if it is not a signed JWS (as expected)
- Claims claims = Jwts.parser()
- .setSigningKey(DatatypeConverter.parseBase64Binary(SECRET))
- .parseClaimsJws(jwt).getBody();
- return claims;
- }
-
- public static void main(String[] args) {
- System.out.println(TokenUtils.createJwtToken("11111"));
- }
- }

3.注入当前登录用户注解
- /**
- * @BelongsProject: JDTaste
- * @BelongsPackage: com.jdtaste.common.util
- * @Author:
- * @CreateTime: 2018-07-04 15:39
- * @Description: 在Controller的方法参数中使用此注解,该方法在映射时会注入当前登录的User对象
- */
- @Target(ElementType.PARAMETER) // 可用在方法的参数上
- @Retention(RetentionPolicy.RUNTIME) // 运行时有效
- public @interface CurrentUser {
- }
4.需要登录的标记注解
- /**
- * @BelongsProject: JDTaste
- * @BelongsPackage: com.jdtaste.common.util
- * @Author:
- * @CreateTime: 2018-07-04 15:38
- * @Description: 在需要登录验证的Controller的方法上使用此注解
- */
- @Target({ElementType.METHOD})// 可用在方法名上
- @Retention(RetentionPolicy.RUNTIME)// 运行时有效
- public @interface LoginRequired {
- }
5.编辑拦截器
- /**
- * @BelongsProject:
- * @BelongsPackage: com.jdtaste.jdtastesso.web.intercepter
- * @Author:
- * @CreateTime: 2018-07-04 09:50
- * @Description: 拦截器
- */
- public class AuthenticationInterceptor implements HandlerInterceptor {
- public final static String ACCESS_TOKEN = "accessToken";
- @Resource
- IUserBaseService userBaseService;
-
- // 在业务处理器处理请求之前被调用
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-
- // 如果不是映射到方法直接通过
- if (!(handler instanceof HandlerMethod)) {
- return true;
- }
- HandlerMethod handlerMethod = (HandlerMethod) handler;
- Method method = handlerMethod.getMethod();
- // 判断接口是否需要登录
- LoginRequired methodAnnotation = method.getAnnotation(LoginRequired.class);
-
- // 有 @LoginRequired 注解,需要认证
- if (methodAnnotation != null) {
- // 判断是否存在令牌信息,如果存在,则允许登录
- String accessToken = request.getHeader("Authorization");
-
-
- if (null == accessToken) {
- throw new CommonException(401, "无token,请重新登录");
- } else {
- // 从Redis 中查看 token 是否过期
- Claims claims;
- try{
- claims = TokenUtils.parseJWT(accessToken);
- }catch (ExpiredJwtException e){
- response.setStatus(401);
- throw new CommonException(401, "token失效,请重新登录");
- }catch (SignatureException se){
- response.setStatus(401);
- throw new CommonException(401, "token令牌错误");
- }
-
- String userName = claims.getId();
- UserBase user = userBaseService.findUserByAccount(userName);
- if (user == null) {
- response.setStatus(401);
- throw new CommonException(401, "用户不存在,请重新登录");
- }
- // 当前登录用户@CurrentUser
- request.setAttribute(CurrentUserConstants.CURRENT_USER, user);
- return true;
- }
-
- } else {//不需要登录可请求
- return true;
- }
- }
- // 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
- @Override
- public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
-
- }
-
- // 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
- @Override
- public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
- }
- }

- /**
- * @BelongsProject:
- * @BelongsPackage: com.jdtaste.jdtastesso.web.intercepter.auth
- * @Author:
- * @CreateTime: 2018-07-04 15:45
- * @Description: 当前用户
- */
- public class CurrentUserConstants {
- /**
- * 当前用户参数名
- */
- public final static String CURRENT_USER = "CurrentUser";
- }
6.自定义参数解析器
- /**
- *
- * @BelongsPackage: com.jdtaste.jdtastesso.web.intercepter.auth
- * @Author:
- * @CreateTime: 2018-07-04 15:42
- * @Description: 自定义参数解析器
- * 增加方法注入,将含有 @CurrentUser 注解的方法参数注入当前登录用户
- */
- public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
- /*
- * supportsParameter:用于判定是否需要处理该参数分解,返回true为需要,并会去调用下面的方法resolveArgument。
- *resolveArgument:真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象。
- *
- * */
- @Override
- public boolean supportsParameter(MethodParameter parameter) {
- System.out.println("----------supportsParameter-----------" + parameter.getParameterType());
- return parameter.getParameterType().isAssignableFrom(UserBase.class)//判断是否能转成UserBase 类型
- && parameter.hasParameterAnnotation(CurrentUser.class);//是否有CurrentUser注解
- }
-
- @Override
- public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
- NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
- System.out.println("--------------resolveArgument-------------" + parameter);
- UserBase user = (UserBase) webRequest.getAttribute(CurrentUserConstants.CURRENT_USER, RequestAttributes.SCOPE_REQUEST);
- if (user != null) {
- return user;
- }
- throw new MissingServletRequestPartException(CurrentUserConstants.CURRENT_USER);
- }
- }

7.将拦截器和参数解析器加入容器
- /**
- * @BelongsProject:
- * @BelongsPackage: com.jdtaste.jdtastesso.conf
- * @Author:
- * @CreateTime: 2018-07-04 10:03
- * @Description: 配置URLInterceptor拦截器,以及拦截路径
- */
- @EnableWebMvc
- @Configuration
- public class MyWebAppConfigurer extends WebMvcConfigurerAdapter {
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- // addPathPatterns 用于添加拦截规则
- // excludePathPatterns 用户排除拦截
- registry.addInterceptor(authenticationInterceptor())
- .addPathPatterns("/*/*");
- super.addInterceptors(registry);
- }
-
- @Override
- public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
- argumentResolvers.add(currentUserMethodArgumentResolver());
- super.addArgumentResolvers(argumentResolvers);
- }
-
- @Bean
- public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
- return new CurrentUserMethodArgumentResolver();
- }
-
- /**
- * 解决 拦截器中注入bean 失败情况出现
- * addArgumentResolvers方法中 添加
- * argumentResolvers.add(currentUserMethodArgumentResolver());
- */
- @Bean
- public AuthenticationInterceptor authenticationInterceptor() {
- return new AuthenticationInterceptor();
- }
- }
- argumentResolvers.add(currentUserMethodArgumentResolver());
- super.addArgumentResolvers(argumentResolvers);
- }
-
- @Bean
- public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
- return new CurrentUserMethodArgumentResolver();
- }
-
- /**
- * 解决 拦截器中注入bean 失败情况出现
- * addArgumentResolvers方法中 添加
- * argumentResolvers.add(currentUserMethodArgumentResolver());
- */
- @Bean
- public AuthenticationInterceptor authenticationInterceptor() {
- return new AuthenticationInterceptor();
- }
- }

8.Controller
- @LoginRequired
- @RequestMapping(value = "/token")
- public String token(@CurrentUser UserBase userBase,String account,String token) {
-
- log.info(account+"----"+token);
- log.info("----"+userBase.getAccount());
- log.info("params==" + userBase.toString());
- if (userBaseService.findUserByAccount(userBase.getAccount()) == null) {
- return "账号不存在";
- } else {
- UserBase result = null;
- result = userBaseService.login(userBase);
- //生成token
- //String accessToken=TokenUtils.createJwtToken(userBase.getAccount());
- if (result == null) {
- return "密码错误";
- } else {
-
- return "SUCCESS";
- }
- }
- }
- @LoginRequired
- @RequestMapping(value = "/token")
- public String token(@CurrentUser UserBase userBase,String account,String token) {
-
- log.info(account+"----"+token);
- log.info("----"+userBase.getAccount());
- log.info("params==" + userBase.toString());
- if (userBaseService.findUserByAccount(userBase.getAccount()) == null) {
- return "账号不存在";
- } else {
- UserBase result = null;
- result = userBaseService.login(userBase);
- //生成token
- //String accessToken=TokenUtils.createJwtToken(userBase.getAccount());
- if (result == null) {
- return "密码错误";
- } else {
-
- return "SUCCESS";
- }
- }
- }

9.前端发送请求 (axios)
- _this.$http.post('/api/user/login',
- this.login,
- {
- headers: {
- Authorization: JSON.parse(sessionStorage.getItem("loginUser")).accessToken,
- }
- }).then((res) => {
- console.log(res);
- });
10 .请求过程
拦截器-->参数解析器-->controler-->拦截器
注意:参数解析器当第一个返回true 才执行第二个方法
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。