shiro有两个作用:登陆控制与权限控制,你可以只选择一种功能去实现。如果想要实现权限控制,那你还要根据你的业务去设计好权限划分。例如某种角色对某模块下某个资源的访问权限。在这里我只是实现了shiro登陆控制,登陆、记住我、注销功能。
前置条件:用户登陆的业务代码完成,可以通过userId或者userName获得用户信息。
1.引入依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>2.继承AuthorizingRealm
import org.apache.log4j.Logger; import org.apache.shiro.authc.*; 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 java.util.List; public class MyShiroRealm extends AuthorizingRealm{ private static Logger log = Logger.getLogger(MyShiroRealm.class); @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private PermissionService permissionService; /** * 授权用户权限 * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { log.debug("##################执行Shiro权限认证##################"); User user = (User) principalCollection.getPrimaryPrincipal(); //把用户所有拥有的角色及权限放入 if(user!=null){ SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //角色 List<Role> roleList = roleService.getRoleByUserId(user.getUserId()); for (Role role:roleList) { info.addRole(String.valueOf(role.getRoleId())); } //权限 List<Permission> permissions = permissionService.getPermissionByRoleId(user.getUserId()); for (Permission p : permissions){ info.addStringPermission(String.valueOf(p.getPermissionId())); } return info; } return null; } /** * 验证用户身份 * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。 * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; log.info("用户验证执行 : "+token.getUsername()); User user = userService.getUserById(Integer.parseInt(token.getUsername())); if(user==null){ log.error("用户 { "+token.getUsername()+" } 不存在 "); throw new AccountException("账户不存在"); } if(user.getStatus()==0){ log.error("用户 { "+token.getUsername()+" } 被禁止登录 "); throw new DisabledAccountException("账号已经禁止登录"); }else{ //更新用户信息 // user.setUpdated(DateUtils.getNowTimestamp()); // user.setUpdatedAt(DateUtils.getNowFormatDate(null)); // System.out.println("效验更新前ROLE:"+user.getRole().getRId()); // userService.update(user,true,user.getId()); } return new SimpleAuthenticationInfo(user,user.getUserPwd(),getName()); } }
3.新增配置类ShiroConfig
import org.apache.shiro.codec.Base64; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { System.out.println("ShiroConfiguration.shirFilter()"); //拦截器. Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>(); // 配置不会被拦截的链接 顺序判断 filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/toLogin", "anon"); //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了 filterChainDefinitionMap.put("/logout", "logout"); //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了; //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--> filterChainDefinitionMap.put("/**", "authc"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/toLogin"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); /* //自定义拦截器 Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>(); //限制同一帐号同时在线的个数。 filtersMap.put("kickout", kickoutSessionControlFilter()); shiroFilterFactoryBean.setFilters(filtersMap); */ return shiroFilterFactoryBean; } @Bean public MyShiroRealm myShiroRealm(){ MyShiroRealm myShiroRealm = new MyShiroRealm(); return myShiroRealm; } @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置realm. securityManager.setRealm(myShiroRealm()); // 自定义缓存实现 使用redis //securityManager.setCacheManager(cacheManager()); // 自定义session管理 使用redis //securityManager.setSessionManager(sessionManager()); //注入记住我管理器; securityManager.setRememberMeManager(rememberMeManager()); return securityManager; } /** * cookie对象; * @return */ public SimpleCookie rememberMeCookie(){ //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); //<!-- 记住我cookie生效时间30天 ,单位秒;--> simpleCookie.setMaxAge(2592000); return simpleCookie; } /** * cookie管理对象;记住我功能 * @return */ public CookieRememberMeManager rememberMeManager(){ CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位) cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag==")); return cookieRememberMeManager; } }
4.测试登陆方法
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; 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; import org.springframework.web.bind.annotation.RestController; @Controller public class LoginController { @RequestMapping(value = "toLogin",method = RequestMethod.GET) public String toLogin(){ return "login"; } @RequestMapping(value="/login",method= RequestMethod.POST) public String login(String userName, String userPwd,String vcode,Boolean rememberMe){ System.out.println(userName); UsernamePasswordToken token = new UsernamePasswordToken(userName, userPwd,rememberMe); SecurityUtils.getSubject().login(token); return "index"; } @RequestMapping(value="/index",method=RequestMethod.GET) public String home(){ Subject subject = SecurityUtils.getSubject(); User principal = (User)subject.getPrincipal(); return "index"; }
不对之处,还望指正,谢谢!