您的位置:首页 > 编程语言 > Java开发

Java Web框架入门(6):shiro

2016-12-01 00:00 477 查看
摘要: 本系列文章对 oschina 上较好的 jfinal、smart-framework 开源框架进行对比分析,比较两个框架的设计方法及优劣,从而对框架的一般设计有所了解。

实现一:封装并实现注解处理器

参看:https://my.oschina.net/myaniu/blog/137198

上文的设计思路:

基于拦截器实现的 shiro权限注解处理

对 shiro提供的五种权限注解 RequiresPermissions、RequiresRoles、RequiresUser、RequiresGuest、RequiresAuthentication进行拦截,自定义处理方法即 authzHandler类

项目启动时,在 shiroPlugin中遍历所有 Action,将上述五种权限注解对应的处理器绑定起来,存入authzMaps<actionKey, authzHandler>

ShiroInterceptor 配置为 globalControllerInterceptors中的第一项,每次都先执行 actionKey对应的权限处理器,实现权限拦截

封装权限验证方法

提供 ShiroMethod工具类,封装权限验证的方法:hasRole\lacksRole\hasAynRoles\hasAllRoles\hasPermission\lacksPermission\authenticated\notAuthenticated\user\guest\principal

保留了 shiroFilter

所以在要做权限验证的 URL较多时,可以在 shiro.ini中配置 [urls]项(URL_Ant_Path_Expression = Path_Specific_Filter_Chain),首次适应原则

小结

也算是比较完整的实现了,侧重点在基于注解的实现方式,但是对 shiro的其他方式也原封不动的保留了

实现二:新的权限拦截约定

参看:http://git.oschina.net/jayqqaa12/JFinal_Authority

封装了上面的 ShiroPlugin,添加插件可提供相同的功能

重写一个 ShiroInterceptor,根据 URL进行拦截(数据库中也用 URL保存权限)

第二种方式算是一种新的思路,但要求数据库中的权限为 URL

另外作者也封装了 shiro的其他功能:最大尝试次数、最多同时在线人数等

实现三:直接使用

如果不要基于注解的权限拦截方式,其实可以完全无侵入的在 shiro.ini中完成所有配置,在 web.xml开启过滤器就完了。顶多封装一个 shiro工具类,实现一个 realm就行了

pom.xml 引入 shiro-web

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>

web.xml启用 ShiroFilter

<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>

<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

添加 shiro.ini配置文件

[main]
#比较密码的方法
credentialsMatcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=2
credentialsMatcher.storedCredentialsHexEncoded=true

#realm,需要自己编写
shiroDbRealm = com.demo.login.ShiroDbRealm
shiroDbRealm.credentialsMatcher = $credentialsMatcher
securityManager.realms = $shiroDbRealm

#[urls]
/login/logout = logout
/login/** = anon
/index/** = authc
/blog/** = authc

编写 realm类,完成 shiro与数据库的交互,实现具体的授权和认证方法

public class ShiroDbRealm extends AuthorizingRealm {

/**
* 认证回调函数,登录时调用,返回的 info 与录入的 token比对
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {

UsernamePasswordToken authcToken = (UsernamePasswordToken) token;
String accountName = authcToken.getUsername();

SystemUser user = SystemUser.dao.findByEmail(accountName);
if (null == user)
throw new AuthenticationException("用户名或者密码错误");
if (user.isLocked())
throw new LockedAccountException("该用户已被锁定");

return new SimpleAuthenticationInfo(user.getEmail(), user.getPwd(),
ByteSource.Util.bytes(user.getEmail() + user.getSalt2()), getName());
}

/**
* 授权查询回调函数,进行鉴权但缓存中无用户的授权信息时调用.
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//User user = (User) principals.fromRealm(getName()).iterator().next();
String username = (String) principals.fromRealm(getName()).iterator().next();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if (null == username) {
return info;
}
SystemUser user = SystemUser.dao.findByEmail(username);
if (null == user) {
return info;
}
List<SystemRole> roles = SystemRole.dao.getRoles(user.getId());
if (CollectionUtils.isNotEmpty(roles)) {
for (SystemRole role : roles) {
// 保存角色的名称及角色的权限
info.addRole(role.getName());
info.addStringPermissions(SystemRes.dao.getResUrls(SystemRes.dao.getReses(role)));
}
}
return info;
}
}

实现登陆功能

做完以上几步后,只用在 LoginController中编写 login、logout方法,在 SystemUser里编写encrypt方法对密码进行加密就行了,所有其他的操作都不需要 shiro了,只用在 shiro.ini 的 [urls]模块指定权限就行了

public class LoginController extends Controller {
private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);
private static final String LOGIN_URL = "/login";
private static final String MAIN_URL = "/blog/1";

public void index() {

if (SystemUser.dao.authenticated()) {
// 调转到主页面
this.redirect(MAIN_URL);
} else {
// 显示登录页
this.createToken("loginToken");
this.render("login.html");
}
}

@Before(LoginValidator.class)
public void login() {
try {
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(email, password);
token.setRememberMe(true);
subject.login(token);
SystemUser user = findByEmail(email);
subject.getSession(true).setAttribute(SESSION_USER_KEY, user);
}

// 调转到主页面
this.redirect(MAIN_URL);
return;
} catch (LockedAccountException e) {
LOGGER.error(e.getMessage());
this.setAttr("msg", "账号已被锁定");
} catch (AuthenticationException e) {
LOGGER.error(e.getMessage());
this.setAttr("msg", "用户名或者密码错误");
} catch (Exception e) {
LOGGER.error(e.getMessage());
this.setAttr("msg", "系统异常");
}
this.keepPara("username");
this.forwardAction(LOGIN_URL);
}

public void logout() {
try {
SystemUser.dao.logout();

this.redirect(LOGIN_URL);
} catch (SessionException ise) {
LOGGER.debug("Encountered session exception during logout.  This can generally safely be ignored.", ise);
} catch (Exception e) {
LOGGER.debug("登出发生错误", e);
}
}
}

注册用户的密码加密方法

public SystemUser encrypt() {
String pwd = this.getPwd();

if (pwd != null) {
String salt2 = new SecureRandomNumberGenerator().nextBytes().toHex();
SimpleHash hash = new SimpleHash("md5", pwd, getEmail() + salt2, 2);
pwd = hash.toHex();
this.setPwd(pwd);
this.setSalt2(salt2);
}

return this;
}

可见通过以上简单五步,就集成了 shiro权限验证框架,具体代码参看 https://git.oschina.net/lan_cyl/smart-jfinal

实现四:深度集成

按照 jfinal的调性,就应该把 shiro给拆开,拿出 ShiroFilter这一条主线

功能:

提供 ShiroUrls类,类似 Routes,保存 URL及对应的权限拦截器。就是 shiro.ini的 urls部分

提取 shiro的所有权限过滤器,作为咱的拦截器

提供 ShiroPlugin完成 [main]部分功能,即配置 shiro的 realm

提供 ShiroInterceptor作为全局拦截器,根据 ShiroUrls执行权限验证

提供 ShiroRealm接口,让用户实现 获取用户、角色、权限的方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: