今天开始学习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加上,但现在还没有这个需求。
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时间:大概月底。
评论