发表于: 2017-08-21 20:28:50

3 1318


今天开始学习shiro的角色管理和授权。


1.shiro的Subject类:代表了一个用户,可以看做一个用户对象的代理对象。应该是一种代理设计模式。


2.shiro的RolesAuthorizationFilter:


shiro web工程中专门用来进行角色管理的过滤器。过滤器先从请求中获得subject,再调用subject.hasAllRoles(roles)方法进行验证。


3.shiro的ShiroHttpServletRequest会把Request包装起来,得到用户名。


4.shiro与web工程整合的关键是弄清楚它的过滤器是怎么用的,在shiro.ini中的配置其实都是对下列过滤器的配置,弄清楚这个之后,自己如果需要在Spring里面配置这些过滤器也就容易很多。在项目中,我用了authc logout roles user四个过滤器,第一个是登陆,第二个是登出,第三个是判断角色,第四个是判断是否是登陆的用户,如果想做的更细一些,也可以把perms加上,但现在还没有这个需求。

Filter Name Class 
anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
logout org.apache.shiro.web.filter.authc.LogoutFilter
noSessionCreation org.apache.shiro.web.filter.session.NoSessionCreationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
user org.apache.shiro.web.filter.authc.UserFilter


5.在roles这个过滤器,默认的是AND关系,也就是如果要实现管理员角色和普通角色都能访问某个URL,需要自定义一个过滤器,把AND改成OR就行了。所以在项目中自定义了一个AnyOfRolesAuthorizationFilter,继成roles,重写isAccessAllowed方法就可以了。


6.在authc这个过滤器中,默认的是一个全栈项目,即登陆错误就回到登陆页面,登陆成功就跳转到主页,并不支持REST接口,也不支持返回JSON,需要实现返回JSON,而不是跳转,则还需要重新写一个Filter,继成authc就可以了。在项目中,重新写了一个FormJsonAuthenticationFilter过滤器,重写了onLoginFailure方法,直接把请求转发给一个controller。controller再根据错误信息返回JSON,跳转由前端完成。

这样,基本上登陆授权角色的需求就都可以通过shiro实现了,至于模块,有需要了可以加进去,暂时就不加了。


shiro.ini


[main]

myRealm = com.ptteng.carrot.yongzheng.util.AuthenticateRealm
securityManager.realm = $myRealm
anyofroles = com.ptteng.carrot.yongzheng.util.AnyOfRolesAuthorizationFilter
jsonauthc = com.ptteng.carrot.yongzheng.util.FormJsonAuthenticationFilter
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
sha256Matcher.storedCredentialsHexEncoded = false
myRealm.credentialsMatcher = $sha256Matcher
jsonauthc.successUrl = /loginMessage
jsonauthc.failUrl = /loginMessage
jsonauthc.loginUrl = /login.jsp
logout.redirectUrl = /login.jsp
[urls]
/login.jsp = jsonauthc
/logout = logout
/a/u/** = user, roles[admin]
/a/** = user, anyofroles["admin,normal”]


AnyOfRolesAuthorizationFilter过滤器


public class AnyOfRolesAuthorizationFilter extends AuthorizationFilter {

    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response,
                                   Object mappedValue) throws IOException {
        final Subject subject = getSubject(request, response);
        final String[] rolesArray = (String[]) mappedValue;
        if (rolesArray == null || rolesArray.length == 0) {
            return true;
        }
        for (String roleName : rolesArray) {
            if (subject.hasRole(roleName)) {
                return true;
            }
        }
        return false;
    }
}


FormJsonAuthenticationFilter过滤器


public class FormJsonAuthenticationFilter extends FormAuthenticationFilter {

    private static final Logger log = LoggerFactory.getLogger(FormJsonAuthenticationFilter.class);
    private String failUrl;
    public void setFailUrl(String failUrl){
        this.failUrl = failUrl;
    }
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
                                     ServletRequest request, ServletResponse response) {
        if (log.isDebugEnabled()) {
            log.debug( "Authentication exception", e );
        }
        setFailureAttribute(request, e);
        try {
            request.getRequestDispatcher(failUrl).forward(request, response);
        } catch (Exception e1) {
            log.error("转发失败,无法获得错误信息JSON");
        }
        return true;
    }
}


登录信息controller


@Controller

public class LoginController {
    @Autowired
    private Gson gson;
    @ResponseBody
    @RequestMapping("/loginMessage")
    public String loginMessageHandler(HttpServletRequest request){
        String s = (String) request.getAttribute("shiroLoginFailure");
        if (s == null){
            return gson.toJson(new RMessage());
        }
        if (s.contains("IncorrectCredentialsException")){
            s = "密码错误";
        }
        if (s.contains("UnknownAccountException")){
            s = "用户名不存在";
        }
        return gson.toJson(new RMessage(s, 1));
    }
}


Shiro Realm实现类:


public class AuthenticateRealm extends AuthorizingRealm {

    private static final Log log = LogFactory.getLog(AuthenticateRealm.class);
    private static final String AUTHENTICATION_QUERY = "SELECT password_token FROM account WHERE name = ?";
    private static final String USER_ROLES_QUERY = "select role.name from account left join role on account.role_id = role.id where account.name = ?";
    private DataSource dataSource;
    public AuthenticateRealm() {
        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        basicDataSource.setUrl("jdbc:mysql://zengtian.cc:3306/carrot_yongzheng");
        basicDataSource.setUsername("root");
        basicDataSource.setPassword("root");
        dataSource = basicDataSource;
    }
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }
        String username = (String) getAvailablePrincipal(principals);
        Connection conn = null;
        Set<String> roleNames;
        try {
            conn = dataSource.getConnection();
            roleNames = getRoleNamesForUser(conn, username);
        } catch (SQLException e) {
            final String message = "There was a SQL error while authorizing user [" + username + "]";
            if (log.isErrorEnabled()) {
                log.error(message, e);
            }
            throw new AuthorizationException(message, e);
        } finally {
            JdbcUtils.closeConnection(conn);
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
        return info;
    }
    //验证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        String username = userToken.getUsername();
        if (username == null) {
            throw new AccountException("Null usernames are not allowed by this realm.");
        }
        Connection conn = null;
        SimpleAuthenticationInfo info = null;
        try {
            conn = dataSource.getConnection();
            PreparedStatement ps = null;
            ps = conn.prepareStatement(AUTHENTICATION_QUERY);
            ps.setString(1, username);
            ResultSet rs = null;
            String password = null;
            rs = ps.executeQuery();
            while (rs.next()) {
                password = rs.getString(1);
            }
            if (password == null) {
                throw new UnknownAccountException("No account found for user [" + username + "]");
            }
            info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName());
        } catch (SQLException e) {
            final String message = "There was a SQL error while authenticating user [" + username + "]";
            log.error(message, e);
            throw new AuthenticationException(message, e);
        } finally {
            JdbcUtils.closeConnection(conn);
        }
        return info;
    }
    //取得角色列表
    private Set<String> getRoleNamesForUser(Connection conn, String username) throws SQLException {
        PreparedStatement ps = null;
        ResultSet rs = null;
        Set<String> roleNames = new LinkedHashSet<String>();
        try {
            ps = conn.prepareStatement(USER_ROLES_QUERY);
            ps.setString(1, username);
            rs = ps.executeQuery();
            while (rs.next()) {
                String roleName = rs.getString(1);
                if (roleName != null) {
                    roleNames.add(roleName);
                } else {
                    if (log.isWarnEnabled()) {
                        log.warn("Null role name found while retrieving role names for user [" + username + "]");
                    }
                }
            }
        } finally {
            JdbcUtils.closeResultSet(rs);
            JdbcUtils.closeStatement(ps);
        }
        return roleNames;
    }

}

————————————————————————————————————————————

开始了解什么是线程,开始学习jdk8的Lambda表达式,因为做算法题的答案里面有遇到过。写了一个HelloWorld,但是不明白为什么调用sleep()方法前面的Thread可以省略。什么情况下调用静态方法可以不用写类?

public class ThreadTest {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            while (true) {
                System.out.println("Thread1 is doing something.");
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            while (true) {
                System.out.println("Thread2 is doing something else.");
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}


明天:修改接口。


问题:接口代码规范不清楚。


总结:无


进度:http://task.ptteng.com/zentao/project-burn-277.html


demo时间:大概月底。


返回列表 返回列表
评论

    分享到