I'm newbie in Spring-World, I have a Spring Boot application with Spring Security and JPA. Also have CrudRepository and UserDetailsService, see below
Application class
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
UserDao
#Repository
public interface UserDao extends CrudRepository<User, Long> {
public Collection<User> findByName(String name);
}
ApiUserDetailsService
#Component
public class ApiUserDetailsService implements UserDetailsService {
#Autowired
private UserDao dao;
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
assert dao != null;
...
}
}
Security Config
#Configuration
#EnableWebSecurity
public class HttpBasicAuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestCache().requestCache(new NullRequestCache());
http.httpBasic();
http.authorizeRequests().anyRequest().authenticated();
}
#Autowired
public void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new ApiUserDetailsService());
}
}
Why Autowired dao is always null? What I do wrong?
You are creating the ApiUserDetailsService manually, in the the method:
#Autowired
public void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new ApiUserDetailsService());
}
What you want is:
#Configuration
#EnableWebSecurity
#EnableJpaRepositories(basePackages = {"your.package.dao"})
public class HttpBasicAuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestCache().requestCache(new NullRequestCache());
http.httpBasic();
http.authorizeRequests().anyRequest().authenticated();
}
// register ApiUserDetailsService as a bean
#Bean
public UserDetailsService apiUserDetailsService() {
return new ApiUserDetailsService();
}
#Autowired
public void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
// get the autowired bean from Spring
auth.userDetailsService(apiUserDetailsService());
}
}
Related
My testclass wit mockmvc seems not to load the springsecuritscontext:
#WebMvcTest(ReportingController.class)
class ReportingControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#BeforeEach
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
}
#WithMockUser(username = "piet", roles = { "ACTUATOR" })
#Test
void test() throws Exception {
Query query = new Query();
ObjectMapper mapper = new ObjectMapper();;
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
mockMvc.perform(post("/statistic/report/test").with(csrf()).sessionAttr("query", query)).andDo(print()).andExpect(view().name("test")).andExpect(status().isOk());
}
}
The test failed with "Failed to load ApplicationContext" .... Error creating bean with name 'webSecurityConfig'. The class webSecurityConfig is my extension for WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LoginSuccessHandler loginSuccessHandler;
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/vendors/**", "/build/**", "/images/**", "/favicon.ico", "/qlocation/**", "/service/**", "/help.html", "/invalid-session.html",
"/robots.txt").permitAll().anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().defaultSuccessUrl("/").successHandler(loginSuccessHandler)
.permitAll();
}
#Autowired
private CustomAuthenticationProvider authProvider;
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
...
}
My assumption is that springSecurity() should load the context or mock it away.
But I'm not sure what is missing.
I'm getting internal server error while running my code. i have no idea what happened because It shows nothing in console.
here my AuthorizationServerConfig class
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private DataSource dataSource;
#Autowired
private AuthenticationManager authenticationManager;
#Bean
TokenStore jdbcTokenStore() {
return new JdbcTokenStore(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()").tokenKeyAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(jdbcTokenStore());
endpoints.authenticationManager(authenticationManager);
}
}
here my websecurityconfig code
#EnableWebSecurity
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
protected AuthenticationManager getAuthenticationManager() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public static PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
this is my userserviceImpl
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserDetailRepository userDetailRepository;
#Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
Optional<User> optionalUser = userDetailRepository.findByUsername(name);
optionalUser.orElseThrow(() -> new UsernameNotFoundException("Username or password wrong"));
UserDetails userDetails = new AuthUserDetail(optionalUser.get());
new AccountStatusUserDetailsChecker().check(userDetails);
return userDetails;
}
}
this is how i get token
http://localhost:8802/oauth/token?grant_type=password&username=abc&scope=READ&password=abc *
output
{
"error": "server_error",
"error_description": "Internal Server Error"
}
When making post request to login this error shows up telling me that userService is null.
When I create an object of UserService instead of autowiring it it passes it but tell me that the repository called in userService is null. The repository is autowired and i cannot instanciate it because its an interface.
Here is the service class:
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
UserService userService;
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new MyUserDetails(userService.getByUsernameOrEmail(s));
}
}
And this is the security configuration class:
Also I am creating an object of MyUserService because spring cannot autowire it telling me that no bean have such name.
#Configuration
#EnableWebSecurity
public class UserSercurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new MyUserDetailsService());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.anyRequest().hasRole(UserType.ADMIN.toString())
.and().formLogin();
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
You cannot instantiate method or variables in your repository as it is an interface but you can autowire your repository class to use your method declared in userRepository interface, you have to autowire your repository class.You can do it this way as I have done in the below code.
Your service class should be like this:
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users user = userRepository.getUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Could not find user");
}
return new MyUserDetails(user);
}
And your repository should be like this:
#Repository
public interface UserRepository extends JpaRepository<Users, Long> {
#Query("SELECT u FROM Users u WHERE u.name = ?1")
public Users getUserByUsername(String username);
}
And also autowire UserDetailsService in your configuration class and pass the instance userDetailsService in your configure(AuthenticationManagerBuilder auth) method, UserDetailsService provides you instance of your MyUserDetailService class.
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.and()
.formLogin().permitAll()
.defaultSuccessUrl("/", true)
.and()
.logout().permitAll()
.logoutSuccessUrl("/");
}
Try to implement in this way, If this is solution of your question please let me know and if you still have doubt feel free to ask.
I'm attempting to use a custom authentication manager but the standard Provider manager is being called to .authenticate. I suspect it has something to do with either the AuthSever or Web Config. Any help is greatly appreciated.
AuthServer configuration:
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private final DataSource dataSource;
#Autowired
public AuthServerConfig(DataSource dataSource){
this.dataSource = dataSource;
}
#Autowired
MicrosJwtConfig microsJwtConfig;
#Autowired
#Qualifier("microsProviderManager")
AuthenticationManager authenticationManager;
public BCryptPasswordEncoder encoder(){
return new BCryptPasswordEncoder(10);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenServices(microsJwtConfig.microsTokenServices())
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.allowFormAuthenticationForClients();
security.passwordEncoder(encoder());
security.tokenKeyAccess("permitAll()");
}
}
WebSecurity config:
#EnableWebSecurity
#Configuration
public class WebSecConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
private MECAuthenticationProvider mecAuthenticationProvider;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return new MicrosProviderManager(clientDetailsService, mecAuthenticationProvider );
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("actuator/health").permitAll().and()
.authorizeRequests().antMatchers("oauth/token").permitAll().and()
.authorizeRequests().antMatchers("actuator/info").permitAll();
}
}
Browser Response for localhost:9999/uaa/oauth/authorize?response_type=code&client_id=acme&redirect_uri=http://example.com is 302 Found, but response for localhost:9999/uaa/login is 401 Unauthorized.
I could get the login token prior to adding the #EnableResourceServer. I am using Spring boot and extending WebSecurityConfigurerAdapter to use authentication Manager with data source. When I tried to add a ResourceServerConfigurerAdapter it wouldn't build. What is the easiest way to allow the login page?
#SpringBootApplication
#RestController
#EnableResourceServer
public class OAuthSvcApplication extends WebMvcConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(OAuthSvcApplication.class);
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
public static void main(String[] args) {
SpringApplication.run(OAuthSvcApplication.class, args);
}
}
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth,DataSource dataSource, Environment env)
throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.checkTokenAccess("hasAuthority('USER')");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("acme")
.secret("acmesecret")
.authorizedGrantTypes("authorization_code",
"refresh_token", "password").scopes("openid");
}
}
}
SpringSecurityFilterChain should always be ordered before other filters.
If you want to add your own authentication for all or some endpoints the best thing to do is add your own WebSecurityConfigurerAdapter with lower order. Modifying the WebSecurityConfigurerAdapter subclass as follows allows the ResourceServer to work with a jdbc authentication mgr:
#Configuration
#Order(-10)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().loginPage("/login").permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager).jdbcAuthentication().dataSource(dataSource);
}
}