- 實現前后端的跨域,我是在后端配置類里實現
- 創建配置類 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好了。