- 实现前后端的跨域,我是在后端配置类里实现
- 创建配置类 WebMvcConfig
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; // 配置全局跨域 @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8081") .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true); } }
- Shiro的配置
- 创建配置类 ShiroConfig
import cn.xej.util.MyPassThruAuthenticationFilter; import cn.xej.util.MyRealm; import cn.xej.util.MySessionManager; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.Filter; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ShiroConfig { /** * 请求拦截 * @param securityManager * @return */ @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, Filter> filtersMap = new HashMap<>(); MyPassThruAuthenticationFilter authFilter = new MyPassThruAuthenticationFilter(); filtersMap.put("authc", authFilter); shiroFilterFactoryBean.setFilters(filtersMap); // 拦截器. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); filterChainDefinitionMap.put("/doLogin", "anon"); filterChainDefinitionMap.put("/**", "authc"); // shiroFilterFactoryBean.setLoginUrl("/unauth"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * @Title: securityManager * @Description: SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理 * @return SecurityManager */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm()); securityManager.setSessionManager(sessionManager()); return securityManager; } /** * 自定义认证 * @Title: myShiroRealm * @Description: ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm,负责用户的认证和权限的处理 * @return MyShiroRealm */ @Bean public MyRealm myRealm() { MyRealm myRealm = new MyRealm(); myRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myRealm; } /** * 密码凭证匹配器,作为自定义认证的基础 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 ) * * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法; hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于 md5(md5("")); hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); return hashedCredentialsMatcher; } /** * 自定义sessionManager,用户的唯一标识,即Token或Authorization的认证 */ @Bean public SessionManager sessionManager() { MySessionManager mySessionManager = new MySessionManager(); return mySessionManager; } }
- 创建MySessionManager类
import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.Serializable; public class MySessionManager extends DefaultWebSessionManager{ private Logger logger = LoggerFactory.getLogger(this.getClass()); private static final String AUTHORIZATION = "Authorization"; private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; public MySessionManager() { super(); } @Override public Serializable getSessionId(ServletRequest request, ServletResponse response) { //前端ajax的headers中必须传入Authorization的值 String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION); //如果请求头中有 Authorization 则其值为sessionId if (!StringUtils.isEmpty(id)) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); logger.info("sessionId="+id); return id; } else { //否则按默认规则从cookie取sessionId logger.info("sessionID为"+id); return super.getSessionId(request, response); } } }
- 过滤器MyPassThruAuthenticationFilter
import org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter; import org.apache.shiro.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; public class MyPassThruAuthenticationFilter extends PassThruAuthenticationFilter{ private Logger log = LoggerFactory.getLogger(this.getClass()); //获取请求方法,若为OPTIONS请求直接返回True放行 @Override public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; if (req.getMethod().equals(RequestMethod.OPTIONS.name())) { log.info("OPTIONS方法直接返回True"); return true; } return super.onPreHandle(request, response, mappedValue); } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { HttpServletResponse httpResp = WebUtils.toHttp(response); HttpServletRequest httpReq = WebUtils.toHttp(request); /** 系统重定向会默认把请求头清空,这里通过拦截器重新设置请求头,解决跨域问题 */ httpResp.addHeader("Access-Control-Allow-Origin", httpReq.getHeader("Origin")); httpResp.addHeader("Access-Control-Allow-Headers", "*"); httpResp.addHeader("Access-Control-Allow-Methods", "*"); httpResp.addHeader("Access-Control-Allow-Credentials", "true"); if (isLoginRequest(request, response)) { return true; } else { saveRequestAndRedirectToLogin(request, response); return false; } } }
- 前端我是使用jquery
$.ajax({ url: base + '/doLogin', type: "post", data: { username: username, password: password }, dataType: 'json', success: function (data) { if (data.status === 200) { alert(data.message); sessionStorage.setItem("sessionId",data.data); window.location = 'main.html'; } else { alert("失败"); location.reload(); } } })
$.ajax({ type: "GET", url: base + '/main', dataType: 'json', beforeSend: function (xhr) { xhr.setRequestHeader("Authorization",sessionId) }, success: function (data) { if(data.status === 200) { $("#info").html(data.message); } } })
- 前面的beforeSend是设置请求头,一定要加。
- 后台控制器用户登录
@PostMapping("/doLogin") public BaseResult doLogin(HttpServletRequest request, @RequestParam("username")String username, @RequestParam("password")String password){ System.out.println(username); System.out.println(password); Map<String, String> map = new HashMap<String, String>(); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username,password); try { subject.login(token); System.out.println(subject.getSession().getId()); String sessionId = (String)subject.getSession().getId(); System.out.println("777" + subject.getSession().getId()); return BaseResult.success("登录成功!",sessionId); }catch (UnknownAccountException e){ System.out.println("账号不存在"); return BaseResult.fail("失败"); }catch (AccountException e){ System.out.println("密码错误"); return BaseResult.fail("失败"); } }
@GetMapping("/main") public BaseResult Main(HttpServletRequest request, HttpServletResponse response){ System.out.println("***********"); MySessionManager mySessionManager = new MySessionManager(); Serializable sessionId = (Serializable) mySessionManager.getSessionId(request,response); System.out.println(sessionId); Subject requestSubject = new Subject.Builder().sessionId(sessionId).buildSubject(); User user = (User) requestSubject.getPrincipal(); List<Role> roleList = userService.getRoleByUserId(user.getUid()); List<Permission> permissionList = null; for (Role r : roleList){ System.out.println(r.getRoleName()); System.out.println("角色id为:"+r.getRid()); permissionList = permissionService.getPermissionByRoleId(r.getRid()); } return BaseResult.success("成功",permissionList); }
- 前后端跨域,以及整合shiro好了。