码迷,mamicode.com
首页 > 其他好文 > 详细

基于Shiro的登录功能 设计思路

时间:2017-10-08 16:50:29      阅读:287      评论:0      收藏:0      [点我收藏+]

标签:维护   hex   primary   dac   count   mes   箭头   es2017   授权   

认证流程

Shiro的认证流程可以看作是个“有窗户黑盒”,

整个流程都有框架控制,对外的入口只有subject.login(token);,这代表“黑盒”

流程中的每一个组件,都可以使用Spring IOC容器里的Bean来替换,以实现拓展化、个性化,这代表“有窗户”。

本示例的认证流程可以参考下图:

技术分享

 

(黑色区域中的红箭头线只代表受关注组件的流程,而不是直接调用,实际上Shiro每个流程的组件都是互相解耦的)

黑色区域里的两块白色区域,就是两个自定义的类。

关键代码如下:

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        AccountPo po = accountMapper.getByUsername(username);
        if (po == null) {
            throw new UnknownAccountException("用户名或密码错误。");
        }
        if (!po.getEnable_flag()) {
            throw new DisabledAccountException("该用户已被禁用。");
        }
        CredentialsDto credentials = new CredentialsDto(po.getPassword(), po.getSalt());
        return new SimpleAuthenticationInfo(username, credentials, getName());
    }

 

    /**
     * 密码匹配
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo info) {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String password = new String(token.getPassword());
        CredentialsDto credentials = (CredentialsDto) info.getCredentials();
        String salt = credentials.getSalt();
        String passwordMd5 = DigestUtils.md5Hex(password + salt);
        boolean result = equals(passwordMd5, credentials.getPasswordMd5());
        if (!result) {
            throw new IncorrectCredentialsException("用户名或密码错误。");
        }
        return true;
    }

 

认证过滤

直接在spring-shiro.xml的shiroFilter.filterChainDefinitions中定义哪些URL需要认证才能访问并不利于维护,

更合适的方法可能是写一个读取器,告诉filterChainDefinitions哪些URL不需要认证即可,需要认证的写在最后

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/" />
        <property name="unauthorizedUrl" value="/html/unauthorized.html" />
        <property name="filterChainDefinitions">
            <value>
                #{filterChainDefinitionsLoader.anonUrls()}
                /**=authc
            </value>
        </property>
    </bean>

 

/**
 * Shiro的FilterChainDefinitions配置加载器
 *
 * @author Deolin
 */
public class FilterChainDefinitionsLoader {

    @Autowired
    private UnauthenticationMapper unauthenticationMapper;

    /**
     * 从数据库`unauthorization`表中读取所有匿名URL
     *
     * @return FilterChainDefinitions内容片段
     */
    public String anonUrls() {
        StringBuilder sb = new StringBuilder(400);
        List<String> urls = authenticationMapper.listUrls();
        for (String url : urls) {
            sb.append(url);
            sb.append("=anon");
            sb.append(CRLF);
        }
        return sb.toString();
    }

}

 

FilterChainDefinitionsLoader.anonUrls()会在项目启动时调用,读取到所有匿名权限URL,

拼接成字符串,返回给filterChainDefinitions

 

授权配置

采用基于角色的权限设计,一个权限对应一个URL。一个用户间接地拥有多个权限,间接地能够访问多个权限所一一对应的URL。

这样一来,权限会全部配置在spring-shiro.xml的shiroFilter.filterChainDefinitions中,例如:

/html/students-view.html=perms["students-view"]

/student/add=perms["studentAdd"]

/student/list=perms["studentList"]

红蓝部分都是可自定义的,项目中每个涉及到权限的URL,都应该定义,

并保存到数据库`permission`表的name字段和url字段。

即`permission`是张字典表,项目中有多少个涉及到权限的URL,表中就会有多少字段。

 

授权配置也需要读取器

        <property name="filterChainDefinitions">
            <value>
                #{filterChainDefinitionsLoader.anonUrls()}
                #{filterChainDefinitionsLoader.permsUrls()}
                /**=authc
            </value>
        </property>

 

    /**
     * 从数据库`permission`表中读取所有权限
     *
     * @return FilterChainDefinitions内容片段
     */
    public String permsUrls() {
        StringBuilder sb = new StringBuilder(400);
        List<PermissionPo> pos = permissionMapper.list();
        for (PermissionPo po : pos) {
            sb.append(po.getUrl());
            sb.append("=perms[\"");
            sb.append(po.getName());
            sb.append("\"]");
            sb.append(CRLF);
        }
        return sb.toString();
    }

 

授权流程

技术分享

 

关键代码如下:

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> roleNames = accountMapper.listRoleNames(username);
        Set<String> permissionNames = accountMapper.listPermissionNames(username);
        info.setRoles(roleNames);
        info.setStringPermissions(permissionNames);
        return info;
    }

 

项目下载

https://github.com/spldeolin/login-demo

 

基于Shiro的登录功能 设计思路

标签:维护   hex   primary   dac   count   mes   箭头   es2017   授权   

原文地址:http://www.cnblogs.com/deolin/p/7637698.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!