在spring-cloud中使用shiro权限控制,分为网关权限控制和后台逻辑控制。
注意:要在后台做权限认证,必须先在网关做身份认证,将身份认证信息使用redis跨域到后台。本教程的后台权限认证不会运行,要运行还要配置redis。业务比较忙,暂时未写redis这块。
网关权限控制:
一.在网关中主要做用户登录身份认证操作。
1.在pom.xml文件中添加:
<!-- 页面拦截时,需要添加的依赖类 ,一下两个类,是访问页面时必须添加的-->
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>2.4.2.1-RELEASE</version>
</dependency>
2.在application.yml中添加
spring:
profiles:
active:
- shiro
3.application-shiro.properties文件中添加如下:
shiro.filter=/css/**,/js/**,/favicon.ico
shiro.logout=/logout
shiro.authc=/**
shiro.loginUrl=/login
shiro.sucessUrl=/index
shiro.errorUrl=/403
4.配置文件ShiroConfig和MyShiroRealm
ShiroConfig内容如下:
@Configuration
public class ShiroConfig {
/** 不需要过滤的文件地址,以逗号(,)分隔 ***/
@Value("${shiro.filter}")
private String strFilter;
/** 配置退出 过滤器 **/
@Value("${shiro.logout}")
private String strLogout;
/** authc:所有url都必须认证通过才可以访问 **/
@Value("${shiro.authc}")
private String strAuthc;
/** 登录地址 **/
@Value("${shiro.loginUrl}")
private String strLoginUrl;
/** 成功后跳转地址 **/
@Value("${shiro.sucessUrl}")
private String strSuccessUrl;
/** 错误地址 **/
@Value("${shiro.errorUrl}")
private String strErrorUrl;
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在
* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
*
* Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
* 3、部分过滤器可指定参数,如perms,roles
*
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不会被拦截的链接 顺序判断
String[] arrayFilter = strFilter.split(",");
Map<String, String> map = new HashMap<String, String>();
for (String str : arrayFilter) {
map.put(str, "anon");
}
filterChainDefinitionMap.put("/gate-ui/static/**", "anon");
filterChainDefinitionMap.putAll(map);
// 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put(strLogout, "logout");
// <!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put(strAuthc, "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl(strLoginUrl);
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl(strSuccessUrl);
// 未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl(strErrorUrl);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 )
*
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于md5(md5(""));
return hashedCredentialsMatcher;
}
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
// 设置md5加密
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
/**
* cookie对象;
* @return
*/
@Bean
public SimpleCookie rememberMeCookie(){
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//<!-- 记住我cookie生效时间30天 ,单位秒;-->
simpleCookie.setMaxAge(3800);
return simpleCookie;
}
/**
* cookie管理对象;记住我功能
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
//cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(myShiroRealm());
//注入记住我管理器;
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
/**
* 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
*
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean(name = "simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");// 数据库异常处理
mappings.setProperty("UnauthorizedException", "403");
r.setExceptionMappings(mappings); // None by default
r.setDefaultErrorView("error"); // No default
r.setExceptionAttribute("ex"); // Default is "exception"
// r.setWarnLogCategory("example.MvcLogger"); // No default
return r;
}
MyShiroRealm内容如下:
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private SecurityShiroService securityShiroService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
/*主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
//获取用户的输入的账号.
String name = (String)token.getPrincipal();
//通过name从数据库中查找 User对象,如果找到,没找到.
//实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
Map<String, Object> map= new HashMap<String, Object>();
map.put("uName", name);
User user = securityShiroService.selectByUserInfo(map);
if(user == null){
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user, //用户名
user.getuPass(), //密码
ByteSource.Util.bytes(user.getuName()+"haohao"),//salt=username+salt
getName() //realm name
);
// 当验证都通过后,把用户信息放在session里
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("userInfo", user);
return authenticationInfo;
二.在后台逻辑控制中主要做操作权限控制,分为角色和方法的操作权限,目前主要做方法的操作权限。
1.在pom.xml文件中添加:
<!-- 页面拦截时,需要添加的依赖类 ,一下两个类,是访问页面时必须添加的-->
<dependency><groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>2.4.2.1-RELEASE</version>
</dependency>
2.在application.yml中添加
spring:
profiles:
active:
- shiro
3.application-shiro.properties文件中添加如下:
shiro.filter=/**shiro.logout=/logout
shiro.authc=/test
shiro.loginUrl=/login
shiro.sucessUrl=/gate-ui/index
shiro.errorUrl=/403
4.配置文件ShiroConfig和MyShiroRealm
ShiroConfig内容如下:
@Configuration
public class ShiroConfig {
/** 不需要过滤的文件地址,以逗号(,)分隔 ***/
@Value("${shiro.filter}")
private String strFilter;
/** 配置退出 过滤器 **/
@Value("${shiro.logout}")
private String strLogout;
/** authc:所有url都必须认证通过才可以访问 **/
@Value("${shiro.authc}")
private String strAuthc;
/** 登录地址 **/
@Value("${shiro.loginUrl}")
private String strLoginUrl;
/** 成功后跳转地址 **/
@Value("${shiro.sucessUrl}")
private String strSuccessUrl;
/** 错误地址 **/
@Value("${shiro.errorUrl}")
private String strErrorUrl;
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在
* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
*
* Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
* 3、部分过滤器可指定参数,如perms,roles
*
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不会被拦截的链接 顺序判断
String[] arrayFilter = strFilter.split(",");
Map<String, String> map = new HashMap<String, String>();
for (String str : arrayFilter) {
map.put(str, "anon");
}
filterChainDefinitionMap.putAll(map);
// 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put(strLogout, "logout");
// <!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put(strAuthc, "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl(strLoginUrl);
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl(strSuccessUrl);
// 未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl(strErrorUrl);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 )
*
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于md5(md5(""));
return hashedCredentialsMatcher;
}
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
// 设置md5加密
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
/**
* cookie对象;
* @return
*/
@Bean
public SimpleCookie rememberMeCookie(){
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//<!-- 记住我cookie生效时间30天 ,单位秒;-->
simpleCookie.setMaxAge(3800);
return simpleCookie;
}
/**
* cookie管理对象;记住我功能
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
//cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(myShiroRealm());
//注入记住我管理器;
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
/**
* 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
*
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean(name = "simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");// 数据库异常处理
mappings.setProperty("UnauthorizedException", "403");
r.setExceptionMappings(mappings); // None by default
r.setDefaultErrorView("error"); // No default
r.setExceptionAttribute("ex"); // Default is "exception"
// r.setWarnLogCategory("example.MvcLogger"); // No default
return r;
}
MyShiroRealm内容如下:
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private SecurityShiroService securityShiroService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User user = (User)principals.getPrimaryPrincipal();
/*添加加上角色名称和角色下面所有拥有的所有权限值*/
authorizationInfo.addRole(user.gettRoleName());
List<Permission> permissions = user.getPermissions();
if(permissions != null && permissions.size()>0){
for(int i=0; i<permissions.size();i++){
authorizationInfo.addStringPermission(permissions.get(i).gettPermission());
}
}
return authorizationInfo;
}
/*主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
return null;
}