pom添加依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
Shiro 配置
package config.compent;
import java.util.HashMap;
import java.util.Map;
import org.apache.shiro.mgt.SecurityManager;
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 com.company.project.interceptor.ShiroRealm;
/**
* @Description: Shiro的管理配置文件
* @author:
* @create:2019年2月25日 上午10:53:57
*/
@Configuration
public class ShiroConfig {
/**
* @Description:将自己的验证方式加入容器
* @return
* @author:
* @update:2019年2月25日 上午11:15:45
*/
@Bean
public ShiroRealm shiroRealm() {
ShiroRealm shiroRealm = new ShiroRealm();
return shiroRealm;
}
/**
* @Description:配置核心安全事务管理器
* @return
* @author:
* @update:2019年2月25日 上午11:09:26
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
return securityManager;
}
/**
* @Description:Filter工厂,设置对应的过滤条件和跳转条件
* @param securityManager
* @return
* @author:
* @update:2019年2月25日 上午11:09:15
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//登录
shiroFilterFactoryBean.setLoginUrl("/login_toLogin");
//登录成功,跳转到首页
shiroFilterFactoryBean.setSuccessUrl("/main/index");
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/login_toLogin");
Map<String,String> map = new HashMap<String, String>();
//anon:url可以匿名访问
//authc: 需要认证才能进行访问
map.put("/static/**", "anon");
map.put("/code.do", "anon");
map.put("/login_login", "anon");
map.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
/**
* @Description:开启shiro aop注解支持;Controller才能使用@RequiresPermissions等
* @param securityManager
* @return
* @author:
* @update:2019年2月25日 上午11:08:21
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
ShiroRealm.java
在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO.
package com.company.project.interceptor;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import com.company.project.entity.system.user.User;
import com.company.project.service.system.login.LoginService;
import com.company.project.util.shiro.SessionSubject;
/**
* @Description:shiro的认证和授权
* @author:
* @create:2019年2月27日 下午2:55:15
*/
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private LoginService loginService;
/**
* @Description:认证用户身份
* @param token
* @return
* @throws AuthenticationException
* @author:
* @update:2019年2月27日 下午2:56:53
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
//获取用户登录的用户ID、密码
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
String userId = token.getUsername();
String password = String.valueOf(token.getPassword());
User user = null;
//根据用户名获取用户信息
user = loginService.getUserByUsername(token.getUsername());
if (user == null) {
throw new UnknownAccountException("There is no user with username of " + userId);
}
if (!password.equals(user.getPassword().toLowerCase())) {
throw new IncorrectCredentialsException("Password for username " + userId + " is incorrect!");
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userId, password, getName());
//将用户信息放到session中
SessionSubject.setCurrentUser(user);
return simpleAuthenticationInfo;
}
/**
* @Description:用户权限授权
* 授权的方法是在碰到<shiro:hasPermission name=''></shiro:hasPermission>标签的时候调用的
* 它会去检测shiro框架中的权限(这里的permissions)是否包含有该标签的name值,如果有,里面的内容显示
* 如果没有,里面的内容不予显示(这就完成了对于权限的认证.)
*
* shiro的权限授权是通过继承AuthorizingRealm抽象类,重载doGetAuthorizationInfo();
* 当访问到页面的时候,链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行
* 所以如果只是简单的身份认证没有权限的控制的话,那么这个方法可以不进行实现,直接返回null即可。
*
* 在这个方法中主要是使用类:SimpleAuthorizationInfo 进行角色的添加和权限的添加。
* authorizationInfo.addRole(role.getRole()); authorizationInfo.addStringPermission(p.getPermission());
*
* 当然也可以添加set集合:roles是从数据库查询的当前用户的角色,stringPermissions是从数据库查询的当前用户对应的权限
* authorizationInfo.setRoles(roles); authorizationInfo.setStringPermissions(stringPermissions);
*
* 就是说如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "perms[权限添加]");
* 就说明访问/add这个链接必须要有“权限添加”这个权限才可以访问
*
* 如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "roles[100002],perms[权限添加]");
* 就说明访问/add这个链接必须要有 "权限添加" 这个权限和具有 "100002" 这个角色才可以访问
* @param pc
* @return
* @author:
* @update:2019年2月27日 下午4:31:50
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
//获取用户
User user = SessionSubject.getCurrentUser();
//添加用户角色
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
String groupIds = user.getGroupId();
System.out.println("groupIds:"+groupIds);
if(groupIds != null){
String[] groupIdArr = groupIds.split(",");
for(String groupId: groupIdArr){
authorizationInfo.addRole(groupId);
}
}
// //获取用户权限
// Set<Permission> permissions = this.permissionMapper.findPermissionsByRoleId(roles);
// //添加权限
// for (Permission permission:permissions) {
// authorizationInfo.addStringPermission(permission.getPermission());
// }
return authorizationInfo;
}
}
编写登录Controller
LoginController.java
@RequestMapping(value = "/login_login", produces = "application/json;charset=UTF-8")
@ResponseBody
public Object login(String userId, String password, String code, HttpServletRequest request) throws Exception {
String errInfo = "success";
//使用shiro加入身份验证
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(userId, password);
try {
//登录操作
subject.login(token);
} catch (UnknownAccountException e) {
errInfo = "usererror"; //用户名不存在
logger.info("There is no user with username of {}",userId,e);
} catch (IncorrectCredentialsException e) {
errInfo = "usererror"; //用户名或密码有误
logger.info("Password for username {} is incorrect",userId,e);
} catch (AuthenticationException e) {
errInfo = "usererror";
logger.info("User of {} authentication failed",userId,e);
}
//用户信息验证成功
if("success".equals(errInfo)){
//删除session中的验证码信息
SessionSubject.removeSecurityCode();
//记录用户登录信息日志
loginService.insertLastLogin(request);
}
return ;
}
/**
* @Description:用户退出
* @return
* @author:
* @update:2019年3月18日 下午3:38:50
*/
@RequestMapping(value = "/logout")
public ModelAndView logout() {
Subject subject = SecurityUtils.getSubject();
subject.logout();
//页面跳转
}
jsp页面
导入jstl标签库:
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<shiro:authenticated>用户[<shiro:principal/>]已身份验证通过 </shiro:authenticated>
<shiro:notAuthenticated> 未身份验证(包括记住我)</shiro:notAuthenticated>
<!-- 用户没有身份验证时显示相应信息,即游客访问信息 -->
<shiro:guest>游客显示的信息</shiro:guest><br/>
<!-- 用户已经身份验证/记住我登录后显示相应的信息 -->
<shiro:user>用户已经登录过了</shiro:user><br/>
<!-- 用户已经身份验证通过,即Subject.login登录成功,不是记住我登录的 -->
<shiro:authenticated>不是记住我登录</shiro:authenticated><br/>
<!-- 显示用户身份信息,通常为登录帐号信息,默认调用Subject.getPrincipal()获取,即Primary Principal -->
<shiro:principal></shiro:principal><br/>
<!--用户已经身份验证通过,即没有调用Subject.login进行登录,包括记住我自动登录的也属于未进行身份验证,与guest标签的区别是,该标签包含已记住用户 -->
<shiro:notAuthenticated>已记住用户</shiro:notAuthenticated><br/>
<!-- 相当于Subject.getPrincipals().oneByType(String.class) -->
<shiro:principal type="java.lang.String"/><br/>
<!-- 如果当前Subject有角色将显示body体内容 name="角色名" -->
<shiro:hasRole name="admin">这是admin角色</shiro:hasRole><br/>
<!-- 如果当前Subject有任意一个角色(或的关系)将显示body体内容。 name="角色名1,角色名2..." -->
<shiro:hasAnyRoles name="admin,vip">用户拥有admin角色 或者 vip角色</shiro:hasAnyRoles><br/>
<!-- 如果当前Subject没有角色将显示body体内容 -->
<shiro:lacksRole name="admin">如果不是admin角色,显示内容</shiro:lacksRole><br/>
<!-- 如果当前Subject有权限将显示body体内容 name="权限名" -->
<shiro:hasPermission name="userInfo:add">用户拥有添加权限</shiro:hasPermission><br/>
<!-- 如果当前Subject没有权限将显示body体内容 name="权限名" -->
<shiro:lacksPermission name="userInfo:add">如果用户没有添加权限,显示的内容</shiro:lacksPermission><br/>
权限功能校验
经过上面的过程,已经可以对用户的身份进行校验,但是这个时候,但是权限控制好像没有什么作用,因为我们使用admin用户登录之后,在浏览器上访问地址 /userInfo/del发现也是可以使用的,其实我们还少了以下步骤,也就是开启注解支持后,在Controller的方法中添加对应权限
@RequiresPermissions("userInfo:del")
@RequestMapping(value = "/del",method = RequestMethod.GET)
@ResponseBody
public String del(Model model) {
userService.del("wangsaichao");
return "删除用户名为wangsaichao用户成功";
}
添加@RequiresPermissions(“userInfo:del”)然后重启项目,再次使用amdin登录之后,在浏览器上调用http://localhost:9090/userInfo/del就会跳转到以下错误页。证明权限校验成功。
原文参考:https://blog.csdn.net/qq_34021712/article/details/80294417