一、后端配置 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);
});