java - Apache Shiro JdbcRealm with JavaConfig and Spring Boot -
i'm trying configure spring boot application use apache shiro security framework. have working propertiesrealm, i'm trying working jdbcrealm , spring boot's built-in h2 database. here's dependencies in pom.xml:
<dependency> <groupid>org.apache.shiro</groupid> <artifactid>shiro-core</artifactid> <version>1.2.3</version> </dependency> <dependency> <groupid>org.apache.shiro</groupid> <artifactid>shiro-spring</artifactid> <version>1.2.3</version> </dependency> <dependency> <groupid>org.apache.shiro</groupid> <artifactid>shiro-web</artifactid> <version>1.2.3</version> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-jdbc</artifactid> </dependency> <dependency> <groupid>com.h2database</groupid> <artifactid>h2</artifactid> </dependency> my schema.sql:
create table if not exists users ( username varchar(256), password varchar(256), enabled boolean ); create table if not exists user_roles ( username varchar(256), role_name varchar(256) ); my data.sql:
insert users (username, password, enabled) values ('user', '04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb', true); insert user_roles (username, role_name) values ('user', 'guest'); and websecurityconfig.java class configures everything:
package security; import org.apache.shiro.authc.credential.hashedcredentialsmatcher; import org.apache.shiro.crypto.hash.sha256hash; import org.apache.shiro.realm.jdbc.jdbcrealm; import org.apache.shiro.spring.lifecyclebeanpostprocessor; import org.apache.shiro.spring.web.shirofilterfactorybean; import org.apache.shiro.web.filter.authc.anonymousfilter; import org.apache.shiro.web.filter.authc.formauthenticationfilter; import org.apache.shiro.web.filter.authc.logoutfilter; import org.apache.shiro.web.filter.authc.userfilter; import org.apache.shiro.web.filter.authz.rolesauthorizationfilter; import org.apache.shiro.web.mgt.defaultwebsecuritymanager; import org.h2.server.web.webservlet; import org.springframework.beans.factory.annotation.autowired; import org.springframework.boot.context.embedded.servletregistrationbean; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; import org.springframework.context.annotation.dependson; import javax.servlet.filter; import javax.sql.datasource; import java.util.hashmap; import java.util.map; @configuration public class websecurityconfig { @bean(name = "shirofilter") public shirofilterfactorybean shirofilter() { shirofilterfactorybean shirofilter = new shirofilterfactorybean(); map<string, string> filterchaindefinitionmapping = new hashmap<>(); filterchaindefinitionmapping.put("/api/health", "authc,roles[guest],ssl[8443]"); filterchaindefinitionmapping.put("/login", "authc"); filterchaindefinitionmapping.put("/logout", "logout"); shirofilter.setfilterchaindefinitionmap(filterchaindefinitionmapping); shirofilter.setsecuritymanager(securitymanager()); shirofilter.setloginurl("/login"); map<string, filter> filters = new hashmap<>(); filters.put("anon", new anonymousfilter()); filters.put("authc", new formauthenticationfilter()); logoutfilter logoutfilter = new logoutfilter(); logoutfilter.setredirecturl("/login?logout"); filters.put("logout", logoutfilter); filters.put("roles", new rolesauthorizationfilter()); filters.put("user", new userfilter()); shirofilter.setfilters(filters); return shirofilter; } @bean(name = "securitymanager") public defaultwebsecuritymanager securitymanager() { defaultwebsecuritymanager securitymanager = new defaultwebsecuritymanager(); securitymanager.setrealm(jdbcrealm()); return securitymanager; } @autowired private datasource datasource; @bean(name = "realm") @dependson("lifecyclebeanpostprocessor") public jdbcrealm jdbcrealm() { jdbcrealm realm = new jdbcrealm(); hashedcredentialsmatcher credentialsmatcher = new hashedcredentialsmatcher(); credentialsmatcher.sethashalgorithmname(sha256hash.algorithm_name); realm.setcredentialsmatcher(credentialsmatcher); realm.setdatasource(datasource); realm.init(); return realm; } @bean public lifecyclebeanpostprocessor lifecyclebeanpostprocessor() { return new lifecyclebeanpostprocessor(); } @bean public servletregistrationbean h2servletregistration() { servletregistrationbean registration = new servletregistrationbean(new webservlet()); registration.addurlmappings("/console/*"); return registration; } } i'm not seeing errors in logs. tried cranking logging adding following application.properties, doesn't much.
logging.level.org.apache.shiro=debug thanks,
matt
there couple problems happening.
lifecyclebeanpostprocessor
the problem due fact lifecyclebeanpostprocessor defined in config class. since beanpostprocessor must initialized eagerly process other beans. furthermore, rest of websecurityconfig needs initialized eagerly since may impact lifecyclebeanpostprocessor.
the problem autowired feature not yet available because beanpostprocessor (i.e. autowiredannotationbeanpostprocessor) too. means datasource null.
since null jdbcrealm going throw nullpointerexception. in turn caught abstractauthenticator , rethrown authenticationexception. defaultwebsecuritymanager (actually parent defaultsecuritymanager) catches invokes onfailedlogin removes "remember me" cookie.
solving lifecyclebeanpostprocessor
the easiest solution ensure infrastructure related beans defined static method. informs spring not need initialize entire configuration class (i.e. websecurityconfig). again
@bean public static lifecyclebeanpostprocessor lifecyclebeanpostprocessor() { return new lifecyclebeanpostprocessor(); } alternatively, can isolate infrastructure related beans in own configuration.
update
shirofilterfactorybean
i didn't realize shirofilterfactorybean implements beanpostprocessor also. pretty interesting case objectfactory implement beanpostprocessor.
the problem preventing loading of data.sql means application not have users in table authentication fail.
the issue data.sql loaded via datasourceinitializedevent. however, due eager initialization of datasource (it dependency of beanpostprocessor) datasourceinitializedevent cannot fired. why see following in logs:
could not send event complete datasource initialization (applicationeventmulticaster not initialized)
ensuring data.sql loads
there few options see insert statements load.
data.sql->schema.sql
the easiest option move contents of data.sql schema.sql. schema.sql still loaded since not require event fired process it. data.sql requires event same mechanism can used load data when jpa initializes schema.
fixing ordering
unfortunately, cannot make definition shirofilterfactorybean static since relies on other bean definitions. fortunately, there no need beanpostprocessor in instance. means can change code return result of factory bean removes beanpostprocessor equation:
@bean(name = "shirofilter") public abstractshirofilter shirofilter() throws exception { shirofilterfactorybean shirofilter = new shirofilterfactorybean(); map<string, string> filterchaindefinitionmapping = new hashmap<>(); filterchaindefinitionmapping.put("/api/health", "authc,roles[guest],ssl[8443]"); filterchaindefinitionmapping.put("/login", "authc"); filterchaindefinitionmapping.put("/logout", "logout"); shirofilter.setfilterchaindefinitionmap(filterchaindefinitionmapping); shirofilter.setsecuritymanager(securitymanager()); shirofilter.setloginurl("/login"); map<string, filter> filters = new hashmap<>(); filters.put("anon", new anonymousfilter()); filters.put("authc", new formauthenticationfilter()); logoutfilter logoutfilter = new logoutfilter(); logoutfilter.setredirecturl("/login?logout"); filters.put("logout", logoutfilter); filters.put("roles", new rolesauthorizationfilter()); filters.put("user", new userfilter()); shirofilter.setfilters(filters); return (abstractshirofilter) shirofilter.getobject(); } insert user
the insert statement found in data.sql incorrect. needs include enabled column. example:
insert users values ('admin', '22f256eca1f336a97eef2b260773cb0d81d900c208ff26e94410d292d605fed8',true);
Comments
Post a Comment