如何實現Shiro基於資料庫的授權認證
從一個正常的設計角度來講,用戶、角色、許可權三者的關係數據兩個多個多:
一個用戶擁有多個角色,一個角色屬於多個用戶;
一個角色擁有多個許可權,一個許可權屬於多個角色;
範例:資料庫的創建腳本:
drop database if exists shirodb;
create database shirodb character set utf8;
use shirodb;
create table member (
mid varchar(50),
password varchar(32),
name varchar(20),
locked int,
constraint pk_mid primary key(mid)
);
insert into member(mid,password,name,locked) values("admin","hello","管理員",0);
insert into member(mid,password,name,locked) values("mldn","java","隔壁老王",0);
create table role (
rid int auto_increment,
title varchar(50),
flag varchar(50),
constraint pk_rid primary key (rid)
);
create table member_role (
mid varchar(50),
rid int
);
create table action (
actid int auto_increment,
title varchar(50),
flag varchar(50),
constraint pk_actid primary key (actid)
);
create table role_action (
rid int,
actid int
);
為了方便進行登錄與授權處理,編寫一個單獨的程序類,這個類也不做業務層或數據層的劃分了,只是做了簡單的功能類,此類可以取得用戶的信息以及角色和許可權數據。
定義一個Member數據表的Vo類:
package com.gwolf.vo;
import java.io.Serializable;
public class Member implements Serializable{
private String mid;
private String password;
private String name;
public String getMid() {
return mid;
}
public void setMid(String mid) {
this.mid = mid;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
定義業務層的實現類:MemberLoginService
package com.gwolf.service;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
import com.gwolf.vo.Member;
public class MemberLoginService {
private Connection connection;
private static final String DBDRIVER="org.gjt.mm.mysql.Driver";
private static final String DBURL="jdbc:mysql://localhost:3306/shirodb";
private static final String DBUSER = "root";
private static final String PASSWORD="root";
private PreparedStatement pstmt;
public MemberLoginService() {
this.connectionDataBase();
}
public Member get(String mid) {
Member vo = null;
try {
String sql = "select mid,password from member where mid=?";
this.pstmt = this.connection.prepareStatement(sql);
this.pstmt.setString(1, mid);
ResultSet rs = this.pstmt.executeQuery();
if(rs.next()) {
vo = new Member();
vo.setMid(rs.getString(1));
vo.setPassword(rs.getString(2));
}
} catch (SQLException e) {
e.printStackTrace();
}
return vo;
}
/**
* 根據用戶名查詢出永不對應的所有的角色數據
* @param mid
* @return
*/
public Set listRolesByMember(String mid) {
Set allRoles = new HashSet();
try {
String sql = "select flag from role where rid in"
+ "(select rid from member_role where mid=?)";
this.pstmt = this.connection.prepareStatement(sql);
this.pstmt.setString(1, mid);
ResultSet rs = this.pstmt.executeQuery();
while(rs.next()) {
allRoles.add(rs.getString(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
return allRoles;
}
/**
* 根據用戶名查詢出所有的許可權數據
* @param mid
* @return
*/
public Set listActionByMember(String mid) {
Set allAction = new HashSet();
try {
String sql = "select flag from action where actid in"
+ "(select actid from role_action where rid"
+ " in(select rid from member_role where mid=?))";
this.pstmt = this.connection.prepareStatement(sql);
this.pstmt.setString(1, mid);
ResultSet rs = this.pstmt.executeQuery();
while(rs.next()) {
allAction.add(rs.getString(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
return allAction;
}
public void close() {
if(this.connection!=null) {
try {
this.connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private void connectionDataBase() {
try {
Class.forName(DBDRIVER);
connection = DriverManager.getConnection(DBURL, DBUSER, PASSWORD);
} catch (Exception e) {
e.printStackTrace();
}
}
}
最終這個程序類所取得的全部的角色和許可權數據都是需要交給Shiro判斷的,在整個的處理過程之中,它的判斷是利用Set集合完成的,所以必須返回Set集合。
public Set listRolesByMember(String mid) {
Set allRoles = new HashSet();
try {
String sql = "select flag from role where rid in"
+ "(select rid from member_role where mid=?)";
this.pstmt = this.connection.prepareStatement(sql);
this.pstmt.setString(1, mid);
ResultSet rs = this.pstmt.executeQuery();
while(rs.next()) {
allRoles.add(rs.getString(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
return allRoles;
}
此時的操作不在是簡單的用戶登錄了,但是現在除了用戶登錄之外還牽扯到許可權的操作,所以整個的自定的Realm類就必須更換一個父類:
認證處理類:AuthenticatingRealm。
授權處理類:AuthorizingRealm。
自定義認證授權Realm:
package com.gwolf.shiro.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import com.gwolf.service.MemberLoginService;
import com.gwolf.vo.Member;
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//登錄認證的方法需要先執行,需要用他來判斷登錄的用戶信息是否合法
String username = (String)token.getPrincipal();
MemberLoginService memberLoginService = new MemberLoginService();
Member vo = memberLoginService.get(username);
memberLoginService.close();
if(vo == null) {
throw new AuthenticationException("改用戶名稱不存在");
}else {
String password = new String((char[])token.getCredentials());
if(vo.getPassword().equals(password)) {
AuthenticationInfo auth = new SimpleAuthenticationInfo(username, password,"memberRealm");
return auth;
}else {
throw new IncorrectCredentialsException("密碼錯誤!");
}
}
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authenticationInfo = new SimpleAuthorizationInfo();
MemberLoginService memberLoginService = new MemberLoginService();
authenticationInfo.setRoles(memberLoginService.listRolesByMember(username));
authenticationInfo.setStringPermissions(memberLoginService.listActionByMember(username));
memberLoginService.close();
return authenticationInfo;
}
}
需要在shiro.ini文件裡面定義相關的Realm配置:
[main]
jdbcRealm=com.gwolf.shiro.realm.MyRealm
securityManager.realm=$jdbcRealm
在單元測試類中測試程序是否正確執行:
package com.gwolf.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
public class TestLoginDemo {
public static void main(String[] args) {
//取得Factory介面對象,主要的目的是通過配置文件載入文之中的信息,這些信息暫時不能成為認證信息
//取得裡面所保存的所有的認證數據信息
SecurityManager securityManager = new IniSecurityManagerFactory("classpath:shiro.ini").getInstance();
//利用一個專門的認證操作的處理類,實現認證處理的具體實現
SecurityUtils.setSecurityManager(securityManager);
//獲取進行用戶名和密碼認證的介面對象
Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken("admin", "hello"));
System.out.println(subject.isPermitted("member:add"));
}
}
TAG:Adolph談JAVA高端 |