Add custom endpoint to Spring OAuth2 Authorization Server - spring

I am interested in adding a custom rest endpoint to my OAuth2 Authorization server.
I want to add a registration endpoint that my UI resource server can call, register a user, and get back a token all in one shot (auto login on registration).
I can make this in two requests since the UI Resource Server has the password of the user, but I would prefer to do it in one, since the I am re-using the Authorization Server to store all my user credentials.
I have created an endpoint like
#FrameworkEndpoint
class RegistrationController {
#Autowired
LocalUserAuthenticationService userDetailsService
#Autowired
TokenEndpoint tokenEndpoint
#RequestMapping(value = "/registration", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<OAuth2AccessToken> registerUser(Principal principal,
#RequestBody #Valid RegistrationRequest registrationRequest) {
userDetailsService.register(registrationRequest.email, registrationRequest.password)
return tokenEndpoint.getAccessToken(principal, [grant_type: 'password', username: registrationRequest.email, password: registrationRequest.password])
}
}
And I register it in my context
#EnableAuthorizationServer
public class AuthServerConfig {
#Bean
public RegistrationController(){
return new RegistrationController()
} ... more
}
However, the request is always unauthorized when it is used this way. It says it cannot find the user. It can resolve the basic auth credentials, but wherever it is looking for them it cannot find them despite this bean being registered within this context.
The documentation for #FrameworkEndpoint says
Use with #RequestMapping and all the other #Controller features (and match with a FrameworkEndpointHandlerMapping in the servlet context)
but i cannot seem to crack how to actually do that. Or if I'm misunderstanding it.
How can I get this properly registered so it works like the other framework beans?

Related

Spring: forwarding to /oauth/token endpoint loses authentication

I'm building a Spring Boot authorization server which needs to generate Oauth2 tokens with two different auth methods. I want to have a different endpoint for each method, but by default Spring only creates /oauth/token, and while it can be changed, I don't think it is possible to have two different paths for it.
As an alternative, I'm trying to create two methods in a controller which do an internal forward to /oauth/token, adding a parameter to the request so I can know where it came from.
I have something like this:
#RequestMapping(value = "/foo/oauth/token", method = RequestMethod.POST)
public ModelAndView fooOauth(ModelMap model) {
model.addAttribute("method", "foo");
return new ModelAndView("forward:/oauth/token", model);
}
This performs the forward correctly, but the auth fails with:
There is no client authentication. Try adding an appropriate authentication filter.
The same request works correctly when sent to /oauth/token directly, so I'm guessing that the problem is that the BasicAuthenticationFilter is not running after the forward.
How can I make it work?
I had exactly the same issue. After some research I found out that the problem was caused by Spring Boot 2, not by Spring Security configurations. According to the Spring Boot 2.0 migration guide:
Spring Security and Spring Session filters are configured for ASYNC, ERROR, and REQUEST dispatcher types.
and the Spring Boot's SecurityFilterAutoConfiguration source code:
#Bean
#ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(
SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name())).collect(Collectors
.collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
}
where the defaults for securityProperties.getFilter().getDispatcherTypes() are defined in SecurityProperties as:
private Set<DispatcherType> dispatcherTypes = new HashSet<>(
Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
Thus by default, Spring Boot configures Spring Security so that its filters will not be applied to FORWARD requests (but only to ASYNC, ERROR and REQUEST), and therefore no security filter will be applied to authenticate the requests when forwarding them to /oauth/token.
The solution is simple. You can either add the following line to your application.properties in order to apply default filters to ALL forwarded requests
spring.security.filter.dispatcher-types=async,error,request,forward
or create your own custom filter chain with a path matcher and dispatcherType=FORWARD to only filter requests that are forwared to /oauth/token.
Looking carefully to the filter chains created for the Oauth endpoints, and for the forwarding controllers, it's easy to see that the latter are missing the BasicAuthenticationFilter, because they aren't authenticated, and auth isn't performed again after the forward.
To solve it, I created a new config like this:
#Configuration
public class ForwarderSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
#Autowired
private FooClientDetailsService fooClientDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
for (AuthorizationServerConfigurer configurerBit : configurers) configurerBit.configure(configurer);
http.apply(configurer);
http
.authorizeRequests()
.antMatchers("/foo/oauth/token").fullyAuthenticated()
.and()
.requestMatchers()
.antMatchers("/foo/oauth/token");
http.setSharedObject(ClientDetailsService.class, fooClientDetailsService);
}
}
This code mimics what Spring Oauth does behind the scenes (here), running identical filter chains with the same authentication options on both endpoints.
When the /oauth/token endpoint finally runs, it finds the auth results that it expects, and everything works.
Finally, if you want to run a different ClientDetailsService on two forwarding endpoints, you just have to create two configuration classes like this one, and replace the ClientDetailsService on the setSharedObject call in each of them. Note that for this, you'll have to set different #Order values in each class.

Spring Security using value of variable in class to authenticate

I am using Spring Security in my application. I am authenticating APIs based on the role (ADMIN, USER).
There is one API endpoint which I would like to restrict access using the value of a variable passed as parameter to it.
I have
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable().exceptionHandling().authenticationEntryPoint(this.unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
I have a call
#PostMapping("/something")
public ResponseEntity<BotResponse> handleRequest(#Valid #RequestBody SomeClass someClass) {
// if someClass.getSomeValue() is not present in the User permissions, then it should give an unauthorized response.
return value(someClass);
}
The User in Spring Security is :
public Class User {
String userId;
String userName;
String authorities;
List<String> someList;
//Getters and setters for variables
}
And the SomeClass used is :
public Class SomeClass {
String someValue;
String userName;
...
// Getters and Setters
}
How do I not allow users based on if the value of someClass.getSomeValue is present in User's someList?
As per your question, one approach would be to get the UserDetails stored in your Spring Security Authentication Context and then check the concerned data in this context object against the value passed as the parameter. I'm assuming that you have all the required values stored in the Security Context.
This check can be done in the endpoint code itself(if you have a small number of such APIs). If there are multiple APIs that need the same logic, you will have to implement either a filter that filters only these API(config can be written in web.xml) or a pointcut(through AOP).
Perhaps you could do such kind of authorization with spring's global method security.
To use Method Level Authorization you need to add the following annotation to your Security Configuration class.
#EnableGlobalMethodSecurity(prePostEnabled = true)
Then apply #PreAuthorize using Spring Expression Language, to your end point. Something like..
#PostMapping("/something")
#PreAuthorize("#someService.checkUserAccess(principal, #someClass)")
public ResponseEntity<BotResponse> handleRequest(#Valid #RequestBody SomeClass someClass) {
// if someClass.getSomeValue() is not present in the User permissions, then it should give an unauthorized response.
return value(someClass);
}
#someService is a Bean which you would autowired in the Controller and define checkUserAccess() method in this been. Something like ..
public boolean checkUserAccess(Pricipal principal, SomeClass someClass) {
// here you can fetch your full user object from db or session (depending on your application architecture)
// apply what ever logic you want to apply, return true if user has access and false if no.
}
Note / Suggestion- You may add this checkUserAccess() method to your existing user service if your application design allows it, and autowire user service in the controller.

Main concepts of spring security with JWT tokens (Spring boot with REST controllers)

Introduction:
I have just started using spring boot. For understanding how it works I have tried to convert my existing project (spring MVC, JSP in frontend) to spring boot approach with REST-controller and AngularJS in frontend.
Facing problem:
During migration I have faced to big problem with security. As I understood the best way for having good security layer now is working with JWT tokens and supporting oauth2, on which there are a lot of posts/tutorials which give different information even about basics of the security layer architecture.
So the question is:
Could someone point out full list of security-layer parts/classes which are needed for having basic (but not hello world) security features for spring boot app with REST controllers. Please don't suggest to use stormpath: I want to implement it myself to get better understanding.
Reasoning of asking this big question here:
I have done my own investigation on this topic, but I thought that most of the links which I have checked contain a lot of bad practices, so possible incorrect architecture of security layer. so I really would like to know some kind of good practice of designing architecture of security layer.
Details on needed features:
I have standard list of features which I want to support.
oauth2 support (but also to have possibility to authenticate without it)
register request (creation of jwt token and returning to client)
login request (acquiring jwt token if user was registered)
logout request (releasing jwt token)
token timeout
multiple roles
business rest controllers which checks for authentication and authorization (could you please give an example portion of code)
business rest controllers which doesn't require security
basic filtering http urls (like excluding "statics" from allowed url addresses)
Current layers of the project:
Below are some additional information about my current project structure:
Currently I have implemented the following modules:
controller: Currently MVC controllers, but I am going to convert them to REST
dto: Possibly will be changed a little bit, because of REST approach
model: Will stay unchanged after conversation
exception: For business logic
repository: Will stay unchanged after conversation
service: Possibly will be changed a little bit, because of micro-services
validator: Will stay unchanged after conversation
other business logic modules
If I understood correctly I will need to add two additional layers here:
configuration: I have already converted some xml configurators to java-configs, but haven't touched security configurators
security: I guest here will be placed authentication/authorization managers/tools. One of the goals of this question is to understand what exactly to place here.
app class with main method in the root package (relative root)
You can start by creating 3 projects.
Auth Server: This will take care of authenticating clients and users, issuing token, revoking token etc.
Rest API: All rest controllers, business logic, persistence layer etc.
Front-end: Angular JS, HTML, CSS etc.
Read about OAuth2 grant types.
We use password authorization grant type when authorization server and client is developed by same organization, or when there is a high degree of trust between the resource owner and the client.
Following are the essential classes you'd need for OAuth2 implementation:
A class which extends AuthorizationServerConfigurerAdapter to configure authorization server.
Here you can configure endpoints like userDetailsService (custom class to load user data by username from database), tokenStore (to store tokens in database and perform operations on it), clientDetailsService (load client details from database; your Rest API project could be client).
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.userDetailsService(userDetailsService);
endpoints.tokenStore(tokenStore);
endpoints.setClientDetailsService(clientDetailsService);
endpoints.accessTokenConverter(accessTokenConverter);
}
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//The expression below easies access to /oauth/check_token endpoint from the default denyAll to isAuthenticated.
oauthServer.checkTokenAccess("isAuthenticated()");
oauthServer.allowFormAuthenticationForClients();
oauthServer.passwordEncoder(passwordEncoder);
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
A class which extends ResourceServerConfigurerAdapter. Here you can configure security configuration for the resource server. Resources would be Rest controllers defined in Auth Servers (like controllers for performing CRUD operation on a user object, endpoint to revoke token; controllers which need to be in Auth Server).
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated(); //To restrict all http requests.
/*http.authorizeRequests().antMatchers("/users/**").permitAll(); //Notice ant matcher here, this tells endpoints which do not require authentication. Lots of http configuration options (like applying filters, cors, csrf etc.) are available here. Please explore*/
}
Check out TokenStore default implementation classes (like JdbcTokenStore, JwtTokenStore). If you'd like to use NoSQL db like Cassandra then provide custom TokenStore implementation.
Following is the sample code snippet for custom Token Store used for Cassandra:
#Override
public void storeAccessToken(final OAuth2AccessToken token, final OAuth2Authentication authentication) {
String refreshToken = null;
if (token.getRefreshToken() != null) {
refreshToken = token.getRefreshToken().getValue();
}
if (readAccessToken(token.getValue()) != null) {
removeAccessToken(token.getValue());
}
final AccessTokenBuilder accessTokenBuilder = new AccessTokenBuilder();
accessTokenRepository.save(accessTokenBuilder
.withAuthenticationId(authenticationKeyGenerator.extractKey(authentication))
.withTokenId(extractTokenKey(token.getValue()))
.withTokenBody(ByteBuffer.wrap(serializeAccessToken(token)))
.withUsername(authentication.getName())
.withClientId(authentication.getOAuth2Request().getClientId())
.withAuthentication(ByteBuffer.wrap(serializeAuthentication(authentication)))
.withRefreshTokenId(extractTokenKey(refreshToken))
.build());
}
#Override
public void storeRefreshToken(final OAuth2RefreshToken refreshToken, final OAuth2Authentication authentication) {
final RefreshTokenBuilder refreshTokenBuilder = new RefreshTokenBuilder();
refreshTokenRepository.save(refreshTokenBuilder
.withTokenId(extractTokenKey(refreshToken.getValue()))
.withTokenBody(ByteBuffer.wrap(serializeRefreshToken(refreshToken)))
.withAuthentication(ByteBuffer.wrap(serializeAuthentication(authentication)))
.build());
}
#Override
public OAuth2Authentication readAuthentication(final OAuth2AccessToken token) {
return readAuthentication(token.getValue());
}
#Override
public OAuth2Authentication readAuthentication(final String token) {
OAuth2Authentication authentication = null;
try {
final AccessToken authAccessToken = accessTokenRepository.findByTokenId(extractTokenKey(token));
authentication = deserializeAuthentication(authAccessToken.getAuthentication().array());
} catch (final IllegalArgumentException e) {
removeAccessToken(token);
}
return authentication;
}
#Override
public OAuth2AccessToken readAccessToken(final String tokenValue) {
final AccessToken accessToken = accessTokenRepository.findByTokenId(extractTokenKey(tokenValue));
return accessToken != null ? deserializeAccessToken(accessToken.getTokenBody().array()) : null;
}
#Override
public OAuth2RefreshToken readRefreshToken(final String tokenValue) {
final RefreshToken refreshToken = refreshTokenRepository.findOne(extractTokenKey(tokenValue));
return refreshToken != null ? deserializeRefreshToken(refreshToken.getTokenBody().array()) : null;
}
#Override
public OAuth2Authentication readAuthenticationForRefreshToken(final OAuth2RefreshToken token) {
return readAuthenticationForRefreshToken(token.getValue());
}
OAuth2Authentication readAuthenticationForRefreshToken(final String tokenValue) {
final RefreshToken refreshToken = refreshTokenRepository.findOne(extractTokenKey(tokenValue));
return refreshToken != null ? deserializeAuthentication(refreshToken.getAuthentication().array()) : null;
}
#Override
public OAuth2AccessToken getAccessToken(final OAuth2Authentication authentication) {
OAuth2AccessToken oAuth2AccessToken = null;
final String key = authenticationKeyGenerator.extractKey(authentication);
final AccessToken accessToken = accessTokenRepository.findOne(key);
if (accessToken != null) {
oAuth2AccessToken = deserializeAccessToken(accessToken.getTokenBody().array());
if (oAuth2AccessToken != null && !key.equals(authenticationKeyGenerator.extractKey(readAuthentication(oAuth2AccessToken.getValue())))) {
removeAccessToken(oAuth2AccessToken.getValue());
storeAccessToken(oAuth2AccessToken, authentication);
}
}
return oAuth2AccessToken;
}
You'd need to declare repository interfaces for db operations. Interfaces which extends CrudRepository. For most of the DB operations we don't need to provide implementation, it is handled by Spring. For Cassandra implementation is in SimpleCassandraRepository class. Sample code for Access Token:
public interface AccessTokenRepository extends CrudRepository<AccessToken, String> {
#Query("SELECT * FROM auth_service.oauth_access_token WHERE token_id = :tokenId ALLOW FILTERING")
AccessToken findByTokenId(#Param("tokenId") String tokenId);
}
Sample code for ClientDetails
public interface ClientDetailsRepository extends CrudRepository<ClientDetails, String> {
}
Please note, we don't need to provide implementation for these interfaces. Regular CRUD queries are already implemented and taken care by Spring.
public interface RefreshTokenRepository extends CrudRepository<RefreshToken, String> {
}
Rest API project
Controllers declared here would get called when request received from frontend (AJAX request from javascript). All business logic and persistence layer would go here.
Here you can think about creating a module, a gateway, which talks to Auth Server. This gateway would be between your Rest API and Auth Server.
You can use RestTemplate to call remote Rest service.
If you need that not any Rest API project can make remote calls to Auth Server, then user client_credentials as well along with password grant type. And, use OAuth2RestTemplate instead of RestTemplate. Sample code:
<bean id="oAuth2RestTemplate" class="org.springframework.security.oauth2.client.OAuth2RestTemplate">
<constructor-arg ref="clientCredentialsResourceDetails"/>
<constructor-arg ref="defaultOAuth2ClientContext"/>
<property name="requestFactory" ref="httpComponentsClientHttpRequestFactory"/>
</bean>
<bean id="httpComponentsClientHttpRequestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<constructor-arg ref="selfSignedHttpsClientFactory"/>
</bean>
<bean id="clientCredentialsResourceDetails" class="org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails">
<property name="accessTokenUri" value="${authentication.service.client.token.url:https://localhost:8443/oauth/token}"/>
<property name="clientId" value="${authentication.service.client.id:testClient}"/>
<property name="clientSecret" value="${authentication.service.client.secret:password}"/>
</bean>
<bean id="defaultOAuth2ClientContext" class="org.springframework.security.oauth2.client.DefaultOAuth2ClientContext"/>
I hope this was helpful.
Not sure if you have seen this, but here is a nice article:
https://www.toptal.com/java/rest-security-with-jwt-spring-security-and-java
. And a project on github, more or less based on that article:
https://github.com/szerhusenBC/jwt-spring-security-demo

Spring - Call a Service method in JSTL

I'm using Spring Security to handle user authentication for my Spring MVC web app. I'm able to get the username from the Authentication object, but my username is the email address, and I want to be able to show the user's actual name in my header.
So I have my custom User class:
class Users{
String name;
String email;
String password;
// getters and setters
}
I thought about using an aop scoped proxy to set the User in the session, as explained in this blog: http://richardchesterwood.blogspot.co.uk/2011/03/using-sessions-in-spring-mvc-including.html . The problem I faced using this approach is that the AuthenticationSuccessHandler is actually a Service and should be stateless. So Spring doesn't autowire a Users object for me in the Service.
So I created a Service method that would get the username (or email) from the Authentication object and return my Users object. This I can use in my Controllers.
#Service
#Transactional
public class UserServiceImpl implements UserService {
#Override
public Users getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User userD = (User)auth.getPrincipal();
Users currentUser = getUserByEmail(userD.getUsername());
return currentUser;
}
}
So is there a way that I can call this Service method from JSTL to get the user's full name, which I can display in my header?
Am also open to suggestions for a better way to implement this.
EDIT:
In my earlier approach using the AuthenticationSuccessHandler, my code goes like this:
#Service("userDetailsService")
#Transactional
public class UserAuthenticationServiceImpl implements AuthenticationSuccessHandler {
#Autowired
Users currentUser;
#Override
public void onAuthenticationSuccess(HttpServletRequest hsr, HttpServletResponse hsr1, Authentication a) throws IOException, ServletException {
User user = (User) a.getPrincipal();
Users user1 = userDao.getUserByEmail(user.getUsername());
currentUser.setName(user1.getName());
currentUser.setUserRoles(user1.getUserRoles());
//currentUser = user1;
}
}
And in my spring-servlet.xml file, I have this:
<bean id="currentUser" class="com.foo.bean.Users" scope="session">
<!-- this next element effects the proxying of the surrounding bean -->
<aop:scoped-proxy/>
</bean>
The problem I'm facing here is that Spring isn't autowiring my currentUser object because the Service isn't in the session scope.
If the only thing you need is the full name just use an AuthenticationSuccessHandler to retrieve the user and add the name to the session (or the full user if you need more then that).
#Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth) throws IOException, ServletException {
User user = (User) auth.getPrincipal();
Users user1 = userDao.getUserByEmail(user.getUsername());
WebUtils.setSessionAttribute(req, "currentUser" user1);
}
Then in your JSP the only thing you need is ${currentUser.username}.
Although I wouldn't suggest stuffing the full user in the session I would suggest just adding the information needed.
WebUtils.setSessionAttribute(req, "currentUsername" user1.getUsername());
Then in your JSP ${currentUsername} saves you a lot of serialization overhead of the session.

Spring Security: Why Authentication is extending Principal?

Spring Security has the assumption of Authentication is a Principal.
public interface Authentication extends Principal, Serializable {}
HttpServletRequest has the method of getUserPrincipal which is responsible for accessing principal object.
Let's consider this case:
public interface RealPrincipal extends Principal {
public Integer getId();
}
Common Module A has Real Principal interface and implementation.
Module A uses Common Module A, Servlet Api and does not depend on Spring Security:
Module B uses Common Module A, Servlet Api and configures Spring Security. This module responsible for security and UserDetails implementation.
Web A uses Module A and Module B.
In order to use request methods, I am ending up with such an implementation:
public ModelAndView someRequestHandler(Principal principal) {
User activeUser = (User) ((Authentication) principal).getPrincipal();
...
}
This is forcing me to have dependency of Spring Security for the Module A and other modules. I believe that a proper servlet api abstraction should not depend on spring security. request.getUserPrincipal should return real principal.
Please explain why org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper returns
Authentication instead of Real Principal.
Edit: I have added Common Module A to my scenario and updated that Module B is responsible for security.
As Luke stated, Spring Security uses the Authentication for the Principal because it implements Principal. It does not use the Authentication#getPrincipal() because it is not guaranteed to be a Principal (it is an Object). In fact, in most situations Spring Security's Authentication#getPrincipal() returns a User (does not implement Principal), a custom UserDetails provided by users of the framework, or a String.
If you want Spring Security to handle this, you will likely need to implement this logic using an HttpServletRequestWrapper as Luke suggested. For example, you could do the following:
public RealPrincipalFilter extends OncePerRequestFilter {
public void doFiter(HttpServletRequest request, HttpServletResponse response, FilterChain) {
chain.doFilter(new RealPrincipalRequestWrapper(request), response);
}
private static final class RealPrincipalRequestWrapper
extends HttpServletRequestWrapper {
public Principal getUserPrincipal() {
Authentication auth = (Authentication) super.getPrincipal();
return auth == null ? null : (RealPrincipal) auth.getPrincipal()
}
}
}
#Configuration
#EnableWebSecurity
public WebSecurityConfig extends WebSecurityConfigurerAdapter {
public configure(HttpSecurity http) {
http
// ... other config ...
.addFilterAfter(new RealPrincipalFilter(), SecurityContextHolderAwareRequestFilter.class);
}
...
}
Alternatively, take a look at my answer on your other question for options to integrate with Spring MVC - Injecting Custom Principal to Controllers by Spring Security
The short answer is that Authentication is a Principal so that it can be used in APIs (such as the servlet API method you mention) which require one.
What does this mean in practice? Not a lot. Java's Principal interface has only one method getName, so if you want to do more than render the user's name, you need to know something more about the implementation.
You should probably think about what you mean when you use the phrases "real principal" and "proper servlet api abstraction". How would you expect to implement your someRequestHandler method if the principal was a "real" one, for example?

Resources