springboot+shiro+jwt+vue配置全攻略

一、后端配置
0.pom.xml

        
            com.auth0
            java-jwt
            3.11.0
        

        
            io.jsonwebtoken
            jjwt
            0.9.1
        
1.shiro配置文件
ShiroConfiguration.java
@Slf4j
@Configuration
public class ShiroConfiguration {

    /**
     * shiro 安全过滤链
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

//        shiroFilterFactoryBean.setUnauthorizedUrl("/login");

        // 添加自己的过滤器并且取名为jwt
        Map filterMap = new HashMap<>();
        filterMap.put("jwt", new JWTFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setUnauthorizedUrl("/401");

        Map filterChainDefinitionMap = new LinkedHashMap();
        // 所有的请求通过我们自己的JWT filter
        filterChainDefinitionMap.put("/**", "jwt");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * web 安全管理器
     * @return
     */
    @Bean(name = "securityManager")
    public DefaultSecurityManager getDefaultSecurityManager(@Qualifier("myRelam") MyRealm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        return securityManager;
    }
    @Bean(name="myRelam")
    public MyRealm getMyRealm() {
        return new MyRealm();
    }

    /**
     * 下面的代码是添加注解支持
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 强制使用cglib,防止重复代理和可能引起代理出错的问题
        // https://zhuanlan.zhihu.com/p/29161098
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

2.jwt过滤器 

JWTFilter.jav
import com.fasterxml.jackson.databind.ObjectMapper;
import com.szht.cbhs.core.http.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
public class JWTFilter extends BasicHttpAuthenticationFilter {

	/**
     * 判断用户是否想要登入。
     * 检测header里面是否包含Authorization字段即可
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        String authorization = req.getHeader("Authorization");
        log.info("判断用户是否想要登录:{}",authorization);
        return authorization != null;
    }

    /**
     *
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authorization = httpServletRequest.getHeader("Authorization");
        log.info("判断用户是否想要登录x:{}",authorization);
        JWTToken token = new JWTToken(authorization);
        // 提交给realm进行登入,如果错误他会抛出异常并被捕获
        try {
            getSubject(request, response).login(token);
            // 如果没有抛出异常则代表登入成功,返回true
        }catch (Exception e){
            return false;
        }
        return true;
    }

    /**
     * 这里我们详细说明下为什么最终返回的都是true,即允许访问
     * 例如我们提供一个地址 GET /article
     * 登入用户和游客看到的内容是不同的
     * 如果在这里返回了false,请求会被直接拦截,用户看不到任何东西
     * 所以我们在这里返回true,Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入
     * 如果有些资源只有登入用户才能访问,我们只需要在方法上面加上 @RequiresAuthentication 注解即可
     * 但是这样做有一个缺点,就是不能够对GET,POST等请求进行分别过滤鉴权(因为我们重写了官方的方法),但实际上对应用影响不大
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        System.out.println("isAccessAllowed方法");
        try{
            return executeLogin(request,response);
        }catch (Exception e){
            System.out.println("错误"+e);
//            throw new ShiroException(e.getMessage());
            responseError(response,"shiro fail");
            return false;
        }
//        if (isLoginAttempt(request, response)) {
//            try {
//                executeLogin(request, response);
//            } catch (Exception e) {
//                response401(request, response);
//            }
//        }
//        return true;
    }

    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    /**
     * 将非法请求跳转到 /401
     */
    private void response401(ServletRequest req, ServletResponse resp) {
        try {
            HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
            httpServletResponse.sendRedirect("/401");
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    private void responseError(ServletResponse response,String msg){

        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setStatus(401);
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json;charset=UTF-8");
        try {
            String rj = "shiro shibai";
            httpResponse.getWriter().append(rj);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.

JWTToken.java
import org.apache.shiro.authc.AuthenticationToken;


public class JWTToken implements AuthenticationToken {
	
	private static final long serialVersionUID = 1L;
	// 秘钥
	private String token;
	
	public JWTToken(String token) {
		this.token = token;
	}
	@Override
	public Object getPrincipal() {
		return token;
	}

	
	@Override
	public Object getCredentials() {
		return token;
	}

}

4.token的生成和解析方式,请注意网上很多方式都是无效的,我花费一天时间试验出来的下面方式有效。

JWTUtil.java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;

import java.util.Calendar;
import java.util.Date;


public class JWTUtil {
	public static final String TOKEN_HEADER = "Authorization";
	public static final String TOKEN_PREFIX = "Bearer ";
	private static final String SECRET = "jwtsecretdemo";

	public static final long EXPIRATION_REMEMBER = 24 * 60 * 60 * 7;


	public static boolean verify(String token, String yhbh, String yhmm) {
		Claims cl = getTokenBody(token);
		String tokenYhbh = cl.get("yhbh").toString();
		String tokenYhmm = cl.get("yhmm").toString();
		if(StringUtils.equals(yhbh,tokenYhbh) && StringUtils.equals(yhmm,tokenYhmm)){
			return true;
		}else{
			return false;
		}
	}

	/**
	 * 从token获取用户编号
	 * @param token
	 * @return
	 */
	public static String getYhbh(String token){
		try {
			return getTokenBody(token).get("yhbh").toString();
		}catch (Exception e){
			return null;
		}
	}

	/**
	 * 从token获取用户密码
	 * @param token
	 * @return
	 */
	public static String getYhmm(String token){
		try {
			return getTokenBody(token).get("yhmm").toString();
		}catch (Exception e){
			return null;
		}
	}

	/**
	 * 取出所有自定义的key,value
	 * */
	private static Claims getTokenBody(String token){
		try {
			return Jwts.parser()
					.setSigningKey(SECRET)
					.parseClaimsJws(token)
					.getBody();
		}catch (Exception e){
			return null;
		}
	}

	public static String createToken(String yhbh,String yhmm){
		Date iatDate = new Date();
		// expire time
		Calendar nowTime = Calendar.getInstance();
		//有10天有效期
		nowTime.add(Calendar.DATE, 10);
		Date expiresDate = nowTime.getTime();
		Claims claims = Jwts.claims();
		claims.put("yhbh", yhbh);
		claims.put("yhmm", yhmm);
		String token = Jwts.builder().setClaims(claims).setExpiration(expiresDate)
				.signWith(SignatureAlgorithm.HS512, SECRET)
				.compact();
		return token;
	}

	public static void main(String[] args) {
		String yhbh = "10001";
		String yhmm = "111";
		String token = createToken(yhbh,yhmm);
		System.out.println("t="+token);
		String re = getYhbh(token);
		System.out.println(re);
	}
}

5.

MyRealm.java
import com.szht.cbhs.business.service.user.UserService;
import com.szht.cbhs.business.vo.user.UserInfo;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;


public class MyRealm extends AuthorizingRealm {

	@Autowired
	private UserService userService;
	
	@Override
	public boolean supports(AuthenticationToken token) {
		return token instanceof JWTToken;
	}

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		String yhbh = JWTUtil.getYhbh(principals.toString());
		UserInfo user = userService.selectUserByYhbh(yhbh);
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		simpleAuthorizationInfo.addRole(user.getJsbh());

		return simpleAuthorizationInfo;
	}

	/**
	 * 默认使用此方法进行用户正确与否验证,错误抛出异常即可
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		String token = (String) authenticationToken.getCredentials();
		// 解密获得username,用于和数据库进行对比
		String yhbh = JWTUtil.getYhbh(token);
		if (yhbh == null) {
			throw new AuthenticationException("token 无效!");
		}

		UserInfo user = userService.selectUserByYhbh(yhbh);
		if (user == null) {
			throw new AuthenticationException("用户"+yhbh+"不存在") ;
		}
		
		if (!JWTUtil.verify(token, yhbh, user.getYhmm())) {
			throw new AuthenticationException("账户密码错误!");
		}
		return new SimpleAuthenticationInfo(token, token, "my_realm");
	}

}

6.登录

UserController.java
import com.szht.cbhs.business.common.service.CommonDataCache;
import com.szht.cbhs.business.common.service.CommonService;
import com.szht.cbhs.business.service.user.UserService;
import com.szht.cbhs.business.vo.user.UserInfo;
import com.szht.cbhs.constant.CbhsSysConst;
import com.szht.cbhs.core.http.Result;
import com.szht.cbhs.core.msg.Message;
import com.szht.cbhs.jwt.JWTUtil;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RestController
public class UserController {

    @Autowired
    private CommonService commonService;
    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public Result login(@RequestBody Map jsonmap,
                        HttpServletResponse response) throws UnsupportedEncodingException {
        String yhbh = jsonmap.get("yhbh");
        String yhmm = jsonmap.get("yhmm");
        UserInfo user = userService.selectUserByYhbh(yhbh);
        if (user.getYhmm().equals(yhmm)) {
            String token = JWTUtil.createToken(yhbh,yhmm);
            response.setHeader("token", token);
            List msgs = new ArrayList();
            Message msg = new Message();
            msg.setMsg_info("登录成功");
            msg.setMsg_type(CbhsSysConst.INFO);
            msgs.add(msg);
            Result rs = new Result();
            rs.setSuccess(true);
            rs.setMessages(msgs);
            return rs;
        } else {
            throw new UnauthorizedException();
        }
    }

    @RequestMapping("/401")
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Result unauthorized() {
        List msgs = new ArrayList();
        Message msg = new Message();
        msg.setMsg_code("401");
        msg.setMsg_info("未授权");
        msg.setMsg_type(CbhsSysConst.ERROR);
        msgs.add(msg);
        Result rs = new Result();
        rs.setSuccess(false);
        rs.setMessages(msgs);
        return rs;
    }
}

二、前端配置

Authorization里放的是用户密码生成的token。

 let url = '/main/pzgl/pzfh/dfhpzjs';
      let data = { pzbh: this.search };
      let headers = { headers: { 'Authorization': 'eyJhbGciOiJIUzUxMiJ9.eyJ5aGJoIjoiMTAwMDEiLCJ5aG1tIjoiMTExIiwiZXhwIjoxNjAzMTY1MDE1fQ.GO4aGX_Ydc5EcImexrlDjbU7qH-XjeYmRmpEGa8z5BKktCaKf2_kWO5T0dIlbISdM_4IBs0s2DWJw1Bn8dlTcw' } };
      axios
        .post(url, data, headers)
        .then((response) => {
          this.dialogDesserts = JSON.parse(JSON.stringify(response.data.data));
          this.dialogDesserts.map((item, index) => {
            item.id = index;
          });
        })
        .catch((error) => {
          console.log(error);
        });

 

你可能感兴趣的:(git,jwt)