shiro有两个作用:登陆控制与权限控制,你可以只选择一种功能去实现。如果想要实现权限控制,那你还要根据你的业务去设计好权限划分。例如某种角色对某模块下某个资源的访问权限。在这里我只是实现了shiro登陆控制,登陆、记住我、注销功能。
前置条件:用户登陆的业务代码完成,可以通过userId或者userName获得用户信息。
1.引入依赖
org.apache.shiro
shiro-spring
1.4.0
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 roleList = roleService.getRoleByUserId(user.getUserId());
for (Role role:roleList) {
info.addRole(String.valueOf(role.getRoleId()));
}
//权限
List 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 filterChainDefinitionMap = new LinkedHashMap<>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/toLogin", "anon");
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
//:这是一个坑呢,一不小心代码就不好使了;
//
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 filtersMap = new LinkedHashMap();
//限制同一帐号同时在线的个数。
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");
//
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";
}