I'm trying to understand the Apache Shiro workflow and how to integrate it into my application. What I can't understand is how and where do I perform the login and then send the redirect? Or does Shiro do this automatically (because I specified the realm in the ini file)? Can I send custom information (user attributes) along with the redirect (via Servlet response rather than backing bean)?
What I understand and have so far:
Add the Shiro Listener and Filter to the web.xml file so it can respond to requests:
<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>
Create a shiro.ini file where you can configure some attributes for Shiro:
[main]
shiro.loginURL = /login.xhtml
myRealm = com.example.shiro.MyRealm
securityManager.realms = $myRealm
[urls]
/account/** = authc
/logout = logout
Create the custom realm class:
@Stateless
@Local(Realm.class)
public class SecurityBean implements Realm{
private EntityManager em;
private TypedQuery<Credential> cQuery;
@Override
public Account getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
try{
cQuery = em.createNamedQuery("getCredentials", Credential.class);
cQuery.setParameter("userName", ((UsernamePasswordToken) token).getUsername());
Credential c = cQuery.getSingleResult();
boolean b = Encryption.compare(new String(((UsernamePasswordToken) token).getPassword()), c.getSalt(), c.getEncryptedPassword());
if(b){
return c;
}else{
throw new AuthenticationException("Passwords do not match.");
}
}catch(Exception e){
throw new AuthenticationException("Error verifying credentials.");
}
}
@Override
public String getName() {
return "User Realm";
}
@Override
public boolean supports(AuthenticationToken token) {
if(token instanceof UsernamePasswordToken){
return true;
}else{
return false;
}
}
}
Where Encryption is a class to encrypt and compare passwords and Credential is a JPA Entity class to store the username and password:
@NamedQueries({@NamedQuery(name = "getCredentials", query = "SELECT c FROM Credential c WHERE c.userName = :userName"),
@NamedQuery(name = "deleteCredentials", query = "DELETE FROM Credential c WHERE c.userName = :userName")})
@Entity
public class Credential implements Serializable, Account{
private static final long serialVersionUID = 2555682746921997545L;
@Id @GeneratedValue
private long id;
private String userName;
private String encryptedPassword;
private String salt;
private User user;
//... getters/setters...
}
Where User is a JPA Entity class that actual stores a user's application information.