Apache Shiro 集成Spring(二)
1、依赖:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
2、自定义Realm:
package com.example.demo_mg.realm;
import org.apache.commons.collections.map.HashedMap;
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.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import java.util.*;
public class TestRealm extends AuthorizingRealm {
//模拟users、user_roles、roles_permissions三张表的查询,实际应用需要查询数据库或缓存
Map<String, String> users = new HashMap<>();
Map<String, Set<String>> user_roles = new HashedMap();
Map<String, Set<String>> roles_permissions = new HashedMap();
// String salt = UUID.randomUUID().toString().replaceAll("-","");
{
//不加盐(与认证对应)
users.put("wzs", new Md5Hash("123456",null, 2).toString());
//加盐
// users.put("wzs", new Md5Hash("123456",salt, 2).toString());
user_roles.put("wzs", new HashSet<>(Arrays.asList("admin", "test")));
roles_permissions.put("admin", new HashSet<>(Arrays.asList("user:delete", "user:update")));
super.setName("TestRealm"); //设置Realm名称,可选
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//从认证信息获取用户名
String username = (String)principalCollection.getPrimaryPrincipal();
//从数据库或缓存中获取角色、权限数据
Set<String> roles = user_roles.get(username);
Set<String> permissions = new HashSet<>();
for (String role : roles) {
Set<String> set;
if((set = roles_permissions.get(role)) != null) {
permissions.addAll(set);
}
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//从主题传过来的认证信息中,获得用户名
String username = (String)authenticationToken.getPrincipal();
//通过用户名从数据库中获取凭证
String password = users.get(username);
if(password != null) {
//不加盐
// return new SimpleAuthenticationInfo(username, password, super.getName());
//加盐
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, super.getName());
// simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(salt));
return simpleAuthenticationInfo;
}
return null;
}
}
3、配置:
package com.example.demo_mg.config;
import com.example.demo_mg.realm.TestRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
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;
import java.util.LinkedHashMap;
@Configuration
public class ShiroConfiguration {
@Bean
public HashedCredentialsMatcher getCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setHashIterations(2);
return credentialsMatcher;
}
@Bean
public TestRealm getRealm(HashedCredentialsMatcher credentialsMatcher) {
TestRealm testRealm = new TestRealm();
testRealm.setCredentialsMatcher(credentialsMatcher);
return testRealm;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(TestRealm testRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(testRealm);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean getShiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("login.html");
shiroFilterFactoryBean.setUnauthorizedUrl("403.html");
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login.html", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/*", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 注解式授权2个bean
* @return
*/
//Shiro生命周期处理器
@Bean
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
4、实体:
package com.example.demo_mg.entity;
public class User {
private String username;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
5、控制器:
package com.example.demo_mg.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class LoginContrller {
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String loginUser(String username, String password) {
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(usernamePasswordToken); //完成登录
//更新用户登录时间,也可以在ShiroRealm里面做
return "index";
} catch(Exception e) {
return "login";//返回登录页面
}
}
@RequestMapping("/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "login";
}
@RequestMapping("/test")
public void test() {
System.out.println("test");
}
@RequiresRoles("admin")
@RequestMapping("/role")
public void role() {
System.out.println("role");
}
@RequiresPermissions("user:delete1")
@RequestMapping("/permission")
public void permission() {
System.out.println("permission");
}
}
6、自定义过滤器,授权继承AuthorizationFilter,认证继承AuthenticatinFilter,可以阅读其源码。
Shiro提供的认证过滤器包括anon,authBasic,authc,user,logout,授权过滤器包括perms,roles,ssl,port,举例perms["user:delete","user:update"],roles["admin","user"],方括号内的角色或权限需要同时满足,否则跳到unauthorizadUrl,ssl是https过滤器。
自定义过滤器例子,角色满足一个即可:
package com.example.demo_mg.filter;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class RoleOrFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
Subject subject = getSubject(servletRequest, servletResponse);
String[] roles = (String[])o;
if(roles == null || roles.length == 0) {
return true;
}
for (String role : roles) {
if(subject.hasRole(role)) {
return true;
}
}
return false;
}
}
拿到subject,o是配置的过滤器方括号里面的数组,return true;表示通过过滤器。
配置类修改使自定义过滤器生效:
@Bean
public ShiroFilterFactoryBean getShiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("login.html");
shiroFilterFactoryBean.setUnauthorizedUrl("403.html");
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login.html", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/filter", "roleOr[admin,user]");
filterChainDefinitionMap.put("/*", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//自定义拦截器
LinkedHashMap<String, Filter> filters = new LinkedHashMap<>();
filters.put("roleOr", getRoleOrFilter());
shiroFilterFactoryBean.setFilters(filters);
return shiroFilterFactoryBean;
}
@Bean
public RoleOrFilter getRoleOrFilter() {
RoleOrFilter roleOrFilter = new RoleOrFilter();
return roleOrFilter;
}
测试:
@RequestMapping("/filter")
public void filter() {
System.out.println("filter");
}