我的春季保安正在运转。它使用我自己的身份验证提供程序,因为身份验证由主机系统完成。
由于我想要基于注释的安全性,我将@EnableGlobalMethodSecurity(securedEnabled = true)
添加到我的servlet配置中。
不幸的是,这触发了 AuthenticationManager是必需的异常。避免此异常的唯一方法是使用configureGlobal
向servlet配置添加inMemoryAuthentication
方法。
显然我不希望这个inMemoryAuthentication,因为应用程序依赖于我自己的主机身份验证。我怎样才能摆脱inMemoryAuthentication?
这是我的servlet配置:
@Configuration
@EnableWebMvc
@EnableGlobalMethodSecurity(securedEnabled = true)
@Import({
FooControllerConfig.class,
})
public class FooServerletConfig {
@Autowired
//@formatter:off
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").authorities("ROLE_USER");
}
//@formatter:on
}
这是安全配置:
@Configuration
@EnableWebMvcSecurity
public class FooSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
//@formatter:off
protected void configure(HttpSecurity http) throws Exception {
http
.authenticationProvider(aS400AuthenticationProvider())
.formLogin()
.loginProcessingUrl("/authorized")
.passwordParameter("password")
.usernameParameter("clientId")
.successHandler(fooAuthenticationSuccessHandler())
.failureHandler(fooAuthenticationFailureHandler())
.and()
.csrf().disable()
.rememberMe()
.rememberMeServices(fooRememberMeServices())
.key(CookieService.LIR_SESSION_COOKIE_NAME)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint());
;
}
//@formatter:on
}
如果缺少configureGlobal
方法,这是我得到的例外:
SEVERE: Servlet /FOO threw load() exception
java.lang.IllegalArgumentException: An AuthenticationManager is required
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.afterPropertiesSet(AbstractSecurityInterceptor.java:121)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1571)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1509)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:296)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:293)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:633)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:651)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:508)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:462)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:160)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1280)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1091)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5176)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5460)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:695)
答案 0 :(得分:0)
而不是auth.inMemoryAuthentication()
,您应该尝试auth.authenticationProvider(new YourAuthenticationProvider())
。
答案 1 :(得分:0)
为需要此服务的其他人造福。
要实现jdbcAuthentication,您需要向数据库表中写入两个查询
Query1: usersByUsernameQuery (设置用于根据用户名查找用户的查询。)
Query2 : authoritiesByUsernameQuery (设置用于根据用户名查找用户权限的查询。)
可能是Java配置或xml配置,您所需要做的就是
1.创建数据源
2.通过注入数据源依赖项并配置usersByUsernameQuery和AuthorityByUsernameQuery,在AuthenticationManagerBuilder
中配置jdbc身份验证。
3.配置HttpSecurity。下面提供了详细信息和默认值
-----配置这些URL的拦截URL模式和授权
Default role of unauthenticated user = ROLE_ANONYMOUS
-----配置表单登录以避免默认的登录屏幕和下面给出的默认行为。
login-page = "/login" with HTTP get
usernameParameter = "username"
passwordParameter = "password"
failureUrl = "/login?error"
loginProcessingUrl= "/login" with HTTP post
successUrl = "/"
-----配置注销以覆盖默认行为。
logoutUrl = "/logout"
logoutSuccessUrl = "/login?logout"
-----配置会话管理以覆盖默认行为
expiredUrl = "/login?expired"
invalidate-session = true //you can set false and use delete-cookies="JSESSIONID"
maximumSessions = The default is to allow any number of sessions for a users.
如果这不足以从我的github存储库下载。 Working copy of Spring security with Java configuration
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter
{
public DataSource dataSource()
{
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/springmvc");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setInitialSize(2);
dataSource.setMaxActive(5);
return dataSource;
}
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception
{
auth.jdbcAuthentication().dataSource(dataSource()).passwordEncoder(passwordEncoder())
.usersByUsernameQuery("select username, password, enabled from userdetails where userName=?")
.authoritiesByUsernameQuery(
"select ud.username as username, rm.name as role from userdetails ud INNER JOIN rolemaster rm ON rm.id = ud.roleId where username = ?");
}
@Override
protected void configure(final HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers("/resources/**", "/", "/login", "/api/**").permitAll()
.antMatchers("/config/*", "/app/admin/*")
.hasRole("ADMIN")
.antMatchers("/app/user/*")
.hasAnyRole("ADMIN", "USER")
.and().exceptionHandling()
.accessDeniedPage("/403")
.and().formLogin()
.loginPage("/login")
.usernameParameter("userName").passwordParameter("password")
.defaultSuccessUrl("/app/user/dashboard")
.failureUrl("/login?error=true")
.and().logout()
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.invalidateHttpSession(true)
.and()
.csrf()
.disable();
http.sessionManagement().maximumSessions(1).expiredUrl("/login?expired=true");
}
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
}
如果这不足以从我的github存储库下载。 Working copy of Spring security with XML configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http auto-config="true" use-expressions="true" create-session="ifRequired">
<csrf disabled="true"/>
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/api/**" access="permitAll" />
<intercept-url pattern="/config/*" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/app/admin/*" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/app/user/*" access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" />
<access-denied-handler error-page="/403" />
<form-login
login-page="/login"
default-target-url="/app/user/dashboard"
authentication-failure-url="/login?error=true"
username-parameter="userName"
password-parameter="password" />
<logout invalidate-session="false" success-handler-ref="customLogoutSuccessHandler"/>
<session-management invalid-session-url="/login?expired=true">
<concurrency-control max-sessions="1" />
</session-management>
</http>
<authentication-manager>
<authentication-provider>
<password-encoder ref="encoder" />
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query=
"select username, password, enabled from userdetails where userName=?"
authorities-by-username-query=
"select ud.username as username, rm.name as role from userdetails ud INNER JOIN rolemaster rm ON rm.id = ud.roleId where username = ?" />
</authentication-provider>
</authentication-manager>
<beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<beans:bean id="customLogoutSuccessHandler" class="com.pvn.mvctiles.configuration.CustomLogoutSuccessHandler" />
<beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
<beans:property name="url" value="jdbc:mysql://localhost:3306/springmvc" />
<beans:property name="username" value="root"/>
<beans:property name="password" value="root"/>
<beans:property name="initialSize" value="2" />
<beans:property name="maxActive" value="5" />
</beans:bean>
</beans:beans>
在这里您需要覆盖loadUserByUsername方法 1.通过在方法中作为争论传递的用户名从数据库加载用户密码。 2.从数据库中为用户加载权限,并构建GrantedAuthority列表 3.通过传递在步骤1和步骤2中获取的密码和授权来创建用户 4.返回UserDetail对象,以便spring容器本身负责认证和授权。
@Component
public class UserDaoImpl implements UserDao, UserDetailsService
{
Logger OUT = LoggerFactory.getLogger(UserDaoImpl.class);
@Autowired
SessionFactory sessionFactory;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
try (Session session = sessionFactory.openSession();)
{
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<DbUserDetails> userCriteria = criteriaBuilder.createQuery(DbUserDetails.class);
Root<DbUserDetails> userRoot = userCriteria.from(DbUserDetails.class);
userCriteria.select(userRoot).where(criteriaBuilder.equal(userRoot.get("userName"), username));
Query<DbUserDetails> userQuery =session.createQuery(userCriteria);
DbUserDetails dbUser = userQuery.getSingleResult();
CriteriaQuery<RoleMaster> roleCriteria = criteriaBuilder.createQuery(RoleMaster.class);
Root<RoleMaster> roleRoot = roleCriteria.from(RoleMaster.class);
roleCriteria.select(roleRoot).where(criteriaBuilder.equal(roleRoot.get("id"), dbUser.getRoleId()));
Query<RoleMaster> roleQuery =session.createQuery(roleCriteria);
RoleMaster role = roleQuery.getSingleResult();
List<GrantedAuthority> authList = new ArrayList<>();
authList.add(new SimpleGrantedAuthority(role.getName()));
return new User(username, dbUser.getPassword(),true, true, true, true, authList);
}
catch (Exception e)
{
OUT.error("Exception - {}", e);
throw new UsernameNotFoundException("Exception caught", e);
}
}
}
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
UserDaoImpl userDaoImpl;
@Autowired
public void configureUserDetailsService(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDaoImpl);
}
...
}
在上一步(实现UserDetailsService)中,仅通过用户名争论(没有获取密码的方式)传递loadUserByUsername方法,但是在通过第三方系统进行身份验证时,它不会为您提供存储的凭据,而是会要求您提供证书。因此,您需要用户名和密码。您可以通过实现AuthenticationProvider来获得它,如下所示。一旦针对第三方系统的身份验证成功,您只需要设置一个令牌(UsernamePasswordAuthenticationToken)并将其返回给Spring Security。
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAuthenticationProvider authProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
...
}
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider
{
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
//Do your authentication
// use the credentials
// and authenticate against the third-party system
if (authenticatedSuccessfully)
{
return new UsernamePasswordAuthenticationToken(
name, password, new ArrayList<>());
}
else
{
return null;
}
}
@Override
public boolean supports(Class<?> authentication)
{
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}