最近新入职了一家公司,不同于其他公司,这里用的是Nutz作为其架构,而最近又要基于它利用shiro去做一个权限验证的操作。这里,不过多的介绍Nutz了,主要以shiro为主,话不多说开始正题。
先来说说接下来要基于shiro去做的是什么东西好了。我们访问一个地址,进入用户登录界面,然后输入用户名密码进入该用户能看到的页面,就是这么一个效果。好,那么要实现刚才说的那个效果主要用到的是什么呢?
1.用户认证:用户身份识别,常被称为用户“登录”;
2.权限设置:访问控制;
如果是第一次接触shiro的同学的话建议去了解下shiro是什么,内外部架构,这里推荐几个网址,当然也可以去下载shiro相关入门文档,我也会在最后附上从网上下载的一个中文文档(不需要积分,要什么自行车?!)
shiro简介
shiro入门
当然了,可能有的童鞋想要速成想直接看代码,用实践去学习。接下来我就放出我写的代码,不过在这之前还是先简单介绍一下用户验证和权限设置这里。首先是用户认证,其重点在于认证,如果你已经浏览过上面两个连接的话相信你已经知道shiro认证的条件是什么,比如username(用户名),password(密码),salt(盐),token之类。一旦我们把用户(subject)交于shiro,那么我们提供这些认证条件之后,那么满足通过之后就是有认证的用户。举个我个人的理解的吧,比如我们去办理很多业务,我们提供了一些能确定我们身份的信息(比如手机号,身份证号之类的),就可以顺畅的办理一样;之后再说说权限设置,这里的话我先结合我们的应用来说,在很多系统里每个用户不可能全都享有全部菜单全部按钮的权限(比如一个只有浏览权限的人,有了去操作业务数据的权限,这就很不合理了)。而shiro就可以让我去控制每个用户(subject),给其设置权限和角色,让其访问属于他这种权限的页面享有他这种权限应有的操作。
好了,进入正片环节,先放张jar包的图,jdk 1.8,tomcat 8 ,开发IDE 是netbeans 最新版
包结构
我省略过nutz的相关配置,直接放出我自定义的Realm的代码,以及相应控制类(可能在别处叫controller不过都一个意思)的实现方法
入口方法类:UserModule.java
@At("/user")
@IocBean
public class UserModule {
private static final Log log = Logs.get();
@Inject
protected Dao dao;
@At
public int count() {
return dao.count(User.class);
}
@GET
@At("/login")
@Ok("jsp:jsp.login")
public void loginPage() {}
@POST
@At
@Ok("jsp:jsp.shiroIndex")
public boolean login(String username, String password, HttpSession session) {
if (Strings.isBlank(username) || Strings.isBlank(password)) {
log.debug("username or password is null");
return false;
}
User user = dao.fetch(User.class, username);
if (user == null) {
log.debug("no such user = " + username);
return false;
}
String tmp = new Sha256Hash(password, user.getSalt()).toHex();
if (!tmp.equals(user.getPassword())) {
log.debug("password is wrong");
return false;
}
//添加token为后来的权限做验证
SecurityUtils.getSubject().login(new SimpleShiroToken(user.getId()));
//或者传递用户名、密码
// UsernamePasswordToken userToken = new UsernamePasswordToken(user.getName(),user.getPassword().toCharArray(),false);
// SecurityUtils.getSubject().login(userToken);
session.setAttribute("me", user);
return true;
}
@At
@Ok("json:{locked:'password|salt'}")
public User me(@Attr("me")User user) {
return user;
}
//测试shiro注解
@At
@RequiresAuthentication
public boolean updateAccount(){
return true;
}
@At
@RequiresGuest
public boolean signUp(){
return false;
}
}
自定义Realm实现类:SimpleAuthorizingRealm.java
public class SimpleAuthorizingRealm extends AuthorizingRealm {
//声明自定义service,并生成getter和setter方法,在getter方法利用Nutz方法获取自定义service
private MyService myService;
protected Dao dao;
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("doGetAuthorizationInfo......");
// null usernames are invalid
// if (principals == null) {
// throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
// }
// int userId = (Integer) principals.getPrimaryPrincipal();
// User user = dao().fetch(User.class, userId);
// if (user == null)
// return null;
//
// SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
// user = dao().fetchLinks(user, null);
// return auth;
//用户名
//String username = (String) principals.fromRealm(getName()).iterator().next();
//Integer username = (Integer) principals.fromRealm(getName()).iterator().next();
getMyService().getStr();
Integer userId = (Integer)principals.fromRealm(getName()).iterator().next();
User user = dao().fetch(User.class, userId.longValue());
String username = user.getName();
//String username = "guest";
/*这些代码应该是动态从数据库中取出的,此处写死*/
if(username!=null&&username.equals("admin")){
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRole("ROLE_ADMIN");//添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色
info.addStringPermission("ROLE_ADMIN:manage");//添加权限
return info;
}
// else{
// SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// info.addRole("ROLE_GUEST");//添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色
// info.addStringPermission("ROLE_GUEST:manage");//添加权限
// return info;
// }
return null;
}
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("doGetAuthenticationInfo......");
SimpleShiroToken upToken = (SimpleShiroToken) token;
//还可以把用户存储到session当中
User user = dao().fetch(User.class, ((Integer)upToken.getPrincipal()).longValue());
if (user == null)
return null;
return new SimpleAccount(user.getId(), user.getPassword(), getName());
}
/**
* 覆盖父类的验证,直接pass
*/
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
}
public SimpleAuthorizingRealm() {
this(null, null);
}
public SimpleAuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
super(cacheManager, matcher);
setAuthenticationTokenClass(SimpleShiroToken.class);
}
public SimpleAuthorizingRealm(CacheManager cacheManager) {
this(cacheManager, null);
}
public SimpleAuthorizingRealm(CredentialsMatcher matcher) {
this(null, matcher);
}
public Dao dao() {
if (dao == null) {
dao = Mvcs.ctx().getDefaultIoc().get(Dao.class, "dao");
return dao;
}
return dao;
}
public void setDao(Dao dao) {
this.dao = dao;
}
public MyService getMyService() {
if (myService == null)
myService = Mvcs.ctx().getDefaultIoc().get(MyService.class);
return myService;
}
public void setMyService(MyService myService) {
this.myService = myService;
}
}
输入用户名:admin,密码:123456,返回如下页面,并从控制台可以看出,用户已经被认证
调用方法:
控制台方法执行:
我们可以看到那个认证方法的输出执行,那么为什么会返回这个页面吗?因为我设置了页面跳转,在认证成功之后,会给用户设置角色和权限,这时候只要在跳转的jsp页面设置shiro的jsp标签进行配置即可(记得引入shiro的标签库),页面如下:
有的童鞋说我不知道shiro的jsp标签是什么,怎么用?别慌,请参看下面的链接,相信定有收益。
shiro标签配置JSP元素权限
认证成功跳转页面为shiroIndex.jsp:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
JSP Page
Hello Admin!
Hello Guest!
结语---
自行发挥:我在入口方法设置token的时候只是用到了一个shiro的简单的实现类,还有很多别的实现类,大家可以根据自己的需要去查看API实现,我在这里就抛砖引玉了;还有看过shiro标签之后,回过头来再结合用户权限我们不难发现,shiro不仅可以根据角色或者权限做控制,还可以更细粒度的去实现根据什么角色什么权限才能看到什么的操作,是不是很有趣?
展翅翱翔:相信仔细看过shiro简介的朋友一定知道shiro不止能实现操作,没错,shiro还可以做加密,单点登录,回话管理等等,虽说是轻量级的但是麻雀虽小五脏俱全嘛,大家不妨去看看这些功能,说不定下一个安全架构师就是你。
最后附上我的数据库字段截图,以及代码和shiro的一个pdf文档,希望对大家有帮助。(请去我上传的资源当中下载,都是不要积分的,都是一个源码一个pdf文档,希望能帮到大家)