添加如下jar包:
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>${shiro.version}</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
web.xml添加Spring对Filter的代理,
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
shiro在spring中的配置信息:
<!--Shiro拦截器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="login.html"/>
<!--设置无权限的地址-->
<property name="unauthorizedUrl" value="view/403.html"/>
<property name="filterChainDefinitions">
<!--从上往下匹配,匹配到即返回-->
<value>
<!--不需要校验-->
/view/login.html = anon
/testLogin = anon
</value>
</property>
</bean>
<!--创建SecurityManager-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customerRealm"/>
</bean>
<bean id="customerRealm" class="com.test.shiro.realm.CustomerRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<!--一次加密-->
<property name="hashIterations" value="1"/>
</bean>
定义了密码加密的方式和加密次数
自定义的realm
public class CustomerRealm extends AuthorizingRealm {
private static Logger logger = LoggerFactory.getLogger(UserController.class);
Map<String, String> userNameAndPassword = new HashMap<>();
{
//1通过MD5加盐加密后
userNameAndPassword.put("user1", "55f312f84e7785aa1efa552acbf251db");
userNameAndPassword.put("user2", "55f312f84e7785aa1efa552acbf251db");
super.setName("customerRealm");
}
Map<String, String> userNameAndRole = new HashMap<>();
{
userNameAndRole.put("user1", "admin");
userNameAndRole.put("user2", "user");
}
Map<String, Set<String>> roleAndPermission = new HashMap<>();
{
Set<String> lstAdminPermission=new HashSet<>();
lstAdminPermission.add("delete");
lstAdminPermission.add("select");
roleAndPermission.put("admin",lstAdminPermission);
Set<String> lstUserPermission=new HashSet<>();
lstUserPermission.add("select");
roleAndPermission.put("user",lstUserPermission);
}
/**
* 授权
*
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName = (String) principals.getPrimaryPrincipal();
logger.info("进行授权认证,传递的用户名:{}", userName);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
logger.info("从数据库中获取权限数据,用户名为{}",userName);
//模拟数据库通过用户名获取角色数据
Set<String> roles = new HashSet<>();
roles.add(userNameAndRole.get(userName));
simpleAuthorizationInfo.setRoles(roles);
//模拟通过角色获取权限数据
Set<String> permissions =roleAndPermission.get(userNameAndRole.get(userName));
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
}
/**
* 认证
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
logger.info("Shiro授权校验-doGetAuthenticationInfo");
//从主体传过来的认证信息中,获取用户名
String userName = (String) authenticationToken.getPrincipal();
logger.info("登陆的用户名{}", userName);
//模拟通过用户名到数据库中获取凭证
String password = userNameAndPassword.get(userName);
if (password == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, password, "customerRealm");
//----------------设置盐----------
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(SALT));
return authenticationInfo;
}
private static final String SALT="salt";
public static void main(String[] args) {
Md5Hash md5Hash = new Md5Hash("1",SALT);
System.out.println(md5Hash.toHex());
}
}
加载md5加密后的密码到内存中,模拟从数据库中读取。定义各个角色及权限,重写认证和授权方法。
登陆接口:
/**
* 登陆--测试Shiro认证
* @param user
* @return
*/
@RequestMapping(value = "/testLogin",method = RequestMethod.POST,produces = "application/json;charset=utf-8")
@ResponseBody
public Object testLogin(User user, HttpServletRequest request, HttpServletResponse response){
logger.info("-----------登陆校验------------------");
Subject subject= SecurityUtils.getSubject();
UsernamePasswordToken token=new UsernamePasswordToken(user.getUsername(),user.getPassword());
//密码校验
try{
token.setRememberMe(true);
subject.login(token);
logger.info("是否已认证:"+subject.isAuthenticated());
}catch (Exception e){
logger.error("校验异常{}",e);
return "登陆失败";
}
return "登陆成功";
}
- 权限控制-通过配置文件的方式
在filterChainDefinitions中添加如下信息
<property name="filterChainDefinitions">
<!--从上往下匹配,匹配到即返回-->
<value>
<!--不需要校验-->
/view/login.html = anon
/testLogin = anon
<!--权限校验-->
<!--测试自定义filter及角色满足一个即可-->
/testPropertiesRoles1 = roles["admin"]
/testPropertiesRoles2 = roles["user"]
/testPerpertiesPerms1 = perms["delete"]
/testPerpertiesPerms2 = perms["select"]
/* = authc
</value>
</property>
定义了/testPropertiesRoles1接口只有admin才能访问,/testPropertiesRoles2只能说user才能访问。testPerpertiesPerms1必须要有delete权限才能访问,testPerpertiesPerms2必须要有select权限才能访问,/testLogin接口不需要鉴权。如果没有权限,会跳转到自定义的403页面。
问题:在角色中,如果配置成如下的形式
/testPropertiesRoles2 = roles["admin","user"]
则只有用户同时具有admin和user的角色才能访问,即判断中为并,需要之定义filter
<!--Shiro拦截器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="login.html"/>
<!--设置无权限的地址-->
<property name="unauthorizedUrl" value="view/403.html"/>
<property name="filterChainDefinitions">
<!--从上往下匹配,匹配到即返回-->
<value>
<!--不需要校验-->
/view/login.html = anon
/testLogin = anon
<!--权限校验-->
<!--测试自定义filter及角色满足一个即可-->
/testPropertiesRoles1 = roles["admin"]
/testPropertiesRoles2 = roles["user"]
/testPerpertiesPerms1 = perms["delete"]
/testPerpertiesPerms2 = perms["select"]
<!--自定义Filter权限控制-->
<!--/testPropertiesRoles2 = rolesOr["admin","user"]-->
/* = authc
</value>
</property>
<property name="filters">
<util:map>
<entry key="rolesOr" value-ref="rolesOrFilter"/>
</util:map>
</property>
</bean>
<!--自定义Filter-解决role中一个用户角色需要满足配置的所有的角色-->
<bean id="rolesOrFilter" class="com.test.shiro.realm.RolesOrFilter"/>
自定义rolesOrFilter。
public class RolesOrFilter extends AuthorizationFilter {
private static Logger logger = LoggerFactory.getLogger(RolesOrFilter.class);
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object o) throws Exception {
Subject subject=getSubject(request,response);
String[] roles=(String[]) o;
if(roles==null||roles.length==0){
logger.info("不需要授权");
return true;
}
for (String role:roles){
if(subject.hasRole(role)){
return true;
}
}
return false;
}
}
四个用于测试权限的接口:
/**
* 只能admin访问
* @return
*/
@RequestMapping(value = "/testPropertiesRoles1",method = RequestMethod.GET)
@ResponseBody
public String testPropertiesRole1(){
logger.info("---------userName----------:{}",SecurityUtils.getSubject().getPrincipal());
return "testPropertiesRoles1";
}
/**
* 只有user可以访问,(配置为如果在shiro配置文件中配置成roles["admin","user"],需要当前用户的角色
* 都满足才能访问)
* @return
*/
@RequestMapping(value = "/testPropertiesRoles2",method = RequestMethod.GET)
@ResponseBody
public String testPropertiesRoles2(HttpServletRequest request){
logger.info("---------userName----------:{}",SecurityUtils.getSubject().getPrincipal());
for (Cookie cookie:request.getCookies()){
logger.info("cookieKey:{},cookieValue:{},maxAge:{}",cookie.getName(),cookie.getValue(),cookie.getMaxAge());
}
return "testPropertiesRoles2";
}
/**
* 测试-必须要有delete的权限才能访问
* @return
*/
@RequestMapping(value = "/testPerpertiesPerms1",method = RequestMethod.GET)
@ResponseBody
public Object testPerpertiesPerms1(){
logger.info("---------userName----------:{}",SecurityUtils.getSubject().getPrincipal());
return "testPerpertiesPerms1";
}
/**
* 测试-必须要有select的权限才能访问
* @return
*/
@RequestMapping(value = "/testPerpertiesPerms2",method = RequestMethod.GET)
@ResponseBody
public Object testPerpertiesPerms2(){
logger.info("---------userName----------:{}",SecurityUtils.getSubject().getPrincipal());
return "testPerpertiesPerms2";
}
- 权限控制-通过注解的方式
//-------------------------注解方式----------------------------
/**
* 测试RequiresRoles注解,必须要admin才能访问
* @return
*/
@RequiresRoles("admin")
@RequestMapping(value = "/testRequireRoles1",method = RequestMethod.GET)
@ResponseBody
public String testRequireRoles1(){
return "testRequireRoles1";
}
/**
* 测试RequiresRoles注解,必须是user才能访问
* @return
*/
@RequiresRoles("user")
@RequestMapping(value = "/testRequireRoles2",method = RequestMethod.GET)
@ResponseBody
public String testRequireRoles2(){
return "testRequireRoles2";
}
已上@RequiresRoles注解说明的一个接口只有admin才能访问,第二个接口只有user才能访问,
没有权限时将会报异常信息
Request processing failed; nested exception is org.apache.shiro.authz.UnauthorizedException: Subject does not have role [user]
参考某课网。