安装方式可以使用依赖管理器也可以直接下载jar文件,本文仅使用最方便,最常用,最小化的方式。最新版本是1.5.0,这里使用次新版本(1.4.2)为例。
Shiro 对应用类型没有要求,权限数据可以直接通过配置文件存储,也可以通过实现对应的接口自行处理数据获取。这里只介绍最常用的方式。
官方参考说明
官方参考实例
使用maven安装依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.2</version>
</dependency>
定义一个用于存储用户权限信息的类
@Data
public class CnUser {
private Integer id;
private String userName;
private String nickName;
private Set<String> roles;
private Set<String> perms;
}
public class CnRealm extends AuthorizingRealm {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
AuthUserService authUserService;
@Autowired
AuthRoleService roleService;
//执行认证逻辑(登录时调用)
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
String username = userToken.getUsername();
String password = String.valueOf(userToken.getPassword());
AuthUser authUser = authUserService.getOne(new QueryWrapper<AuthUser>().eq(AuthUser.USER_NAME,username));
logger.warn("认证:{}", authUser);
//用户不存在
if (authUser == null) {
return null;
}
//帐户已经锁定
if (authUser.getIsEnable() != 1) {
throw new LockedAccountException();
}
CnUser shiroUser = BeanUtil.toBean(authUser, CnUser.class);
shiroUser.setRoles(roleService.getRoles(shiroUser.getId()));
logger.info("shiroUser:{}", shiroUser);
//密码错误会自动抛异常
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(shiroUser, authUser.getPassword(), getName());
return info;
}
//执行授权(验证权限时调用)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
CnUser shiroUser = CnUserUtils.getUserInfo();
if (shiroUser == null) {
logger.error("userInfo is null");
return null;
}
logger.info("授权:{}", shiroUser);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//这里只验证角色,如果需要权限继续配置就可以
info.setRoles(shiroUser.getRoles());
return info;
}
}
这里是 Shiro 配置的核心,在统一的配置类中配置
@Configuration
public class ShiroConfig {
//创建ShiroFilterFactoryBean
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager webSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(webSecurityManager);
//添加shiro内置过滤器
/*
* anon:表示可以匿名使用。
authc:表示需要认证(登录)才能使用,没有参数
roles:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
perms:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest:根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。
authcBasic:没有参数表示httpBasic认证
ssl:表示安全的url请求,协议为https
user:当登入操作时不做检查
*/
Map<String, String> fMap = new LinkedHashMap<String, String>();
//资源页面
fMap.put("/css/**", "anon");
fMap.put("/img/**", "anon");
fMap.put("/fonts/**", "anon");
fMap.put("/js/**", "anon");
fMap.put("/lib/**", "anon");
fMap.put("/assets/**", "anon");
//登陆页
fMap.put("/login", "anon");
//公共页
fMap.put("/", "anon");
fMap.put("/index", "anon");
fMap.put("/error", "anon");
fMap.put("/unauthorized", "anon");
//注销
fMap.put("/logout", "logout");
//管理端
fMap.put("/admin/**", "authc");
//个人主页
fMap.put("/**", "anon");
//被拦截返回登录页面
shiroFilterFactoryBean.setLoginUrl("/login");
//授权拦截返回页面
shiroFilterFactoryBean.setUnauthorizedUrl("/login");
shiroFilterFactoryBean.setFilterChainDefinitionMap(fMap);
return shiroFilterFactoryBean;
}
@Bean(name = "defaultWebSecurityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") CnRealm userRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
//创建Realm
@Bean(name="userRealm")
public CnRealm userRealm() {
return new CnRealm();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager securityManager) {
// 上面配置的 DefaultAdvisorAutoProxyCreator 相当于一个切面,下面这个类就相当于切点了,两个一起才能实现注解权限控制。
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
Shiro 官方只提供了 JSP 标签,并未提供 freemarker,需要自行开发标签,或采用开源项目。这里使用一个开源标签库。
<dependency>
<groupId>net.mingsoft</groupId>
<artifactId>shiro-freemarker-tags</artifactId>
<version>1.0.1</version>
<exclusions>
<exclusion>
<artifactId>shiro-core</artifactId>
<groupId>org.apache.shiro</groupId>
</exclusion>
</exclusions>
</dependency>
然后在配置类中进行 freemarker 配置,这个配置中同时支持了 jdk8 的时间类型。
@Configuration
public class FreeMarkerConfig {
@Autowired
private freemarker.template.Configuration configuration;
@PostConstruct
public void setSharedVariable() {
try {
configuration.setSharedVariable("shiro", new ShiroTags());
configuration.setObjectWrapper(new CustomObjectWrapper());
} catch (Exception e) {
e.printStackTrace();
}
}
private static class CustomObjectWrapper extends DefaultObjectWrapper {
@Override
public TemplateModel wrap(Object obj) throws TemplateModelException {
if (obj instanceof LocalDateTime) {
Timestamp timestamp = Timestamp.valueOf((LocalDateTime) obj);
return new SimpleDate(timestamp);
}
if (obj instanceof LocalDate) {
Date date = Date.valueOf((LocalDate) obj);
return new SimpleDate(date);
}
if (obj instanceof LocalTime) {
Time time = Time.valueOf((LocalTime) obj);
return new SimpleDate(time);
}
return super.wrap(obj);
}
}
}
权限标签如下:
<@shiro.guest>
您当前是游客,<a href="javascript:void(0);">登录</a>
</@shiro.guest>
<@shiro.user>
欢迎[<@shiro.principal/>]登录,<a href="/logout.shtml">退出</a>
</@shiro.user>
<@shiro.authenticated>
用户[<@shiro.principal/>]已身份验证通过
</@shiro.authenticated>
<@shiro.notAuthenticated>
当前身份未认证(包括记住我登录的)
</@shiro.notAuthenticated>
return new SimpleAuthenticationInfo(user,user.getPswd(), getName());
<!--取到username--> <@shiro. principal/>
<!--需要指定property--> <@shiro.principal property="username"/>
User user = (User) SecurityUtils.getSubject().getPrincipals();
String username = user.getUsername();
<@shiro.hasRole name="admin">
用户[<@shiro.principal/>]拥有角色admin<br/>
</@shiro.hasRole>
<@shiro.hasAnyRoles name="admin,user,member">
用户[<@shiro.principal/>]拥有角色admin或user或member<br/>
</@shiro.hasAnyRoles>
<@shiro.lacksRole name="admin">
用户[<@shiro.principal/>]不拥有admin角色
</@shiro.lacksRole>
<@shiro.hasPermission name="user/add">
用户[<@shiro.principal/>]拥有user/add权限
</@shiro.hasPermission>
<@shiro.lacksPermission name="user/add">
用户[<@shiro.principal/>]不拥有user/add权限
</@shiro.lacksPermission>