1:前后端分离:更改以前的ajax请求,由于目前的项目基本上都是前后端分离的,所以我们所有的数据都已JSON格式返回给前端,对没有登录的请求进行拦截,覆盖掉shiro原本的跳转到login.jsp页面,继承FormAuthenticationFilter
public class AjaxPermissionsAuthorizationFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
JSONObject jsonObject = new JSONObject();
jsonObject.put("returnCode", ErrorEnum.E_20011.getErrorCode());
jsonObject.put("returnMsg", ErrorEnum.E_20011.getErrorMsg());
PrintWriter out = null;
HttpServletResponse res = (HttpServletResponse) response;
try {
res.setCharacterEncoding("UTF-8");
res.setContentType("application/json");
out = response.getWriter();
out.println(jsonObject);
} catch (Exception e) {
} finally {
if (null != out) {
out.flush();
out.close();
}
}
return false;
}
@Bean
public FilterRegistrationBean registration(AjaxPermissionsAuthorizationFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
}
2:自定义Realm,重写两个接口方法,doGetAuthenticationInfo(登录)和doGetAuthorizationInfo(授权)
登录:拿数据匹配,查询数据库通过,创建SimpleAuthenticationInfo对象传入用户名和密码,调用SecurityUtils.getSubject().getSession.setAttribute()存session
授权:调用SecurityUtils.getSubject.getSession()获取session,从session中获取到用户的权限,构建SimpleAuthorizationInfo对象,授权方法addStringPermissions()方法进行授权
public class UserRealm extends AuthorizingRealm {
Logger logger= LoggerFactory.getLogger("UserRealm");
@Autowired
private LoginService loginService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
Session session=SecurityUtils.getSubject().getSession();
JSONObject perssions= (JSONObject) session.getAttribute(Constants.SESSION_USER_PERMISSION);
logger.info("用户的permissions:"+perssions);
logger.info("用户的权限为:"+perssions.get("permissionList"));
SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermissions((Collection<String>) perssions.get("perssionList"));
return simpleAuthorizationInfo;
}
/**
* 登录
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String loginName= (String) authenticationToken.getPrincipal();
String password= (String) authenticationToken.getCredentials();
JSONObject user=loginService.getUser(loginName,password);
if(user==null){
//用户不存在
throw new UnknownAccountException();
}
//用户存在
SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(
user.getString("username"),
user.getString("password"),
getName());
user.remove("password");
//将用户信息放入session中
SecurityUtils.getSubject().getSession().setAttribute(Constants.SESSION_USER_INFO,user);
return authenticationInfo;
}
}
Shiro配置类:自定义shiro的过滤器链 Map结构
anon:他对应过滤器链里面是空的,不用做什么操作
authc:必须授权认证后的接口才可以访问,他是Shiro内置的一个拦截器FormAuthenFilter
@Configuration
public class ShiroConfiguration {
private Logger logger= LoggerFactory.getLogger("ShiroConfiguration");
/**
* 全场最重要的一步shiro的web过滤器Factory
* 自定义Shiro过滤链,Map结构
* Map中key(xml中是指value值)的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的
* anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种
* authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
//shiro的安全接口
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String,Filter> filterMap=new LinkedHashMap<>();
//此处为自定义ajax
filterMap.put("authc", new AjaxPermissionsAuthorizationFilter());
shiroFilterFactoryBean.setFilters(filterMap);
Map<String,String> filterChainDefinitionMap=new LinkedHashMap<>();
/**
*过滤链定义,从上向下顺序执行,一般将 / ** 放在最为下边:这是一个坑呢,一不小心代码就不好使了;
*authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
*/
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/login/auth", "anon");
filterChainDefinitionMap.put("/login/logout", "anon");
filterChainDefinitionMap.put("/error", "anon");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
//第一步
/**
* 不指定名字的话,自动创建一个方法名第一个字母小写的bean
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm());
return securityManager;
}
/**
* Shiro Realm 继承自AuthorizingRealm的自定义Realm,即指定Shiro验证用户登录的类为自定义的
*/
@Bean
public UserRealm userRealm() {
UserRealm userRealm = new UserRealm();
return userRealm;
}
/**
* 第四部:凭证匹配器
*/
@Bean(name = "credentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//散列的次数,比如散列两次,相当于 md5(md5(""));
hashedCredentialsMatcher.setHashIterations(2);
//storedCredentialsHexEncoded默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码
hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return hashedCredentialsMatcher;
}
/**
* 第三步,管理shiro的生命周期
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**第二步:开启Shiro注解(如@RequiresRoles,@RequiresPerssions,
* 借助SpringAOP扫描使用Shiro注解的类
*/
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
}