CharResponseWrapper captured content is empty - spring

I have tried the following example to replace some content in my servlet response.
Programming Customized Requests and Responses
test.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"></meta>
<link th:href="#{/css/test.css}" rel="stylesheet"></link>
<title>Test</title>
</head>
<body>
<p class="forbiddenClass">Test!</p>
</body>
</html>
test.css:
.forbiddenClass {
color: red;
}
CharResponseWrapper.java
public class CharResponseWrapper extends HttpServletResponseWrapper {
private final CharArrayWriter output;
public CharResponseWrapper(final HttpServletResponse response) {
super(response);
output = new CharArrayWriter();
}
public String toString() {
return output.toString();
}
public PrintWriter getWriter() {
return new PrintWriter(output);
}
}
ClassReplacementFilter.java
#Component
public class ClassReplacementFilter extends GenericFilterBean {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
final CharResponseWrapper wrapper = new CharResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, wrapper);
String content = wrapper.toString();
if (StringUtils.isEmpty(content)) {
System.out.println("content is empty for content type: " + response.getContentType());
} else {
content = content.replaceAll("forbiddenClass", "correctClass");
response.setContentLength(content.getBytes().length);
response.getOutputStream().write(content.getBytes());
}
}
}
As you might see, I want to replace the string forbiddenClass with correctClass, but it only works for the html file. The content of test.css does not change and the following message of the filter is printed to output.
content is empty for content type: text/css;charset=UTF-8
Why is the content empty for test.css?

Why is the content empty for test.css?
Because you only captured whatever is written to response.getWriter(), not whatever is written to response.getOutputStream().
You need the HttpServletResponseWrapper implementation as shown in bottom of this answer to a related question: Catch-all servlet filter that should capture ALL HTML input content for manipulation, works only intermittently.

Related

Can's show one login page after logout

My app use frame.
here index.html:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="${appName}">Template title</title>
<link th:href="#{/public/style.css}" rel="stylesheet"/>
</head>
<frameset cols="15%,*">
<frame src="navigator" name="navigator" scrolling="no" noresize/>
<frame src="welcome" name="main"/>
</frameset>
</html>
Here login controller:
#Controller
public class LoginController {
#Value("${spring.application.name}")
private String appName;
private static Logger logger = LogManager.getLogger(LoginController.class);
/*-
#RequestMapping("/")
#ResponseBody
public String index() {
return "Hello!";
}
*/
// Login form
#RequestMapping("/login.html")
public String login(Model model) {
logger.info("open_login.html");
model.addAttribute("appName", appName);
return "login.html";
}
// Login form with error
#RequestMapping("/login-error.html")
public String loginError(Model model) {
model.addAttribute("appName", appName);
model.addAttribute("loginError", true);
return "login.html";
}
}
and here result:
and after success login (show frames)
When I press button logout then call my custom logout handler:
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
private static Logger logger = LogManager.getLogger(CustomLogoutSuccessHandler.class);
#Override
public void onLogoutSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
request.getSession().invalidate();
response.sendRedirect(request.getContextPath() + "/login.html");
}
}
But here result after press Logout
But I need only ONE page (login.html, without frames)
You will have to add below javascript to the login.html.
if ( window.location !== window.parent.location ) {
// We're deeper than one down
window.parent.location=window.location
}

Spring OAuth2 SSO preflight request handling

I am studying Spring OAuth and got some troubles with CORS and preflight requests, probably someone could help me.
As a base I took example project from "Cloud Native Java" book:
https://github.com/cloud-native-java/edge
For my question two parts are relevant: Gateway SSO service (greetings-client) and Authorization service (auth-service).
Here is SSO configuration:
#Configuration
#EnableOAuth2Sso
class SsoConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests()
.antMatchers( "/", "/app.js", "/login**", "/webjars/**").permitAll().anyRequest()
.authenticated().and().logout().logoutSuccessUrl("/").permitAll().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
Edge service has simple UI part. When called directly it tries to make a request to /user endpoint, which is protected, to get Principal info.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>Edge Service</title>
<meta name="description" content=""/>
<meta name="viewport" content="width=device-width"/>
<base href="/"/>
<script type="text/javascript"
src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript"
src="/webjars/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript"
src="/webjars/angularjs/angular.min.js"></script>
</head>
<body ng-app="app" ng-controller="home as home">
<div class="container" ng-show="!home.authenticated">
Login
</div>
<div class="container" ng-show="home.authenticated">
<!--1-->
Logged in as:
<b><span ng-bind="home.user"></span></b> <br/>
Token:
<b><span ng-bind="home.token"></span> </b><br/>
Greeting from Zuul Route: <b>
<span ng-bind="home.greetingFromZuulRoute"></span></b> <br/>
Greeting from Edge Service (Feign):
<b><span ng-bind="home.greetingFromEdgeService"></span></b><br/>
</div>
<!--2-->
<script type="text/javascript" src="app.js"></script>
</body>
</html>
And javascript:
var app = angular.module("app", []);
//<1>
app.factory('oauth', function () {
return {details: null, name: null, token: null};
});
app.run(['$http', '$rootScope', 'oauth', function ($http, $rootScope, oauth) {
$http.get("/user").success(function (data) {
oauth.details = data.userAuthentication.details;
oauth.name = oauth.details.name;
oauth.token = data.details.tokenValue;
// <2>
$http.defaults.headers.common['Authorization'] = 'bearer ' + oauth.token;
// <3>
$rootScope.$broadcast('auth-event', oauth.token);
});
}]);
app.controller("home", function ($http, $rootScope, oauth) {
var self = this;
self.authenticated = false;
// <4>
$rootScope.$on('auth-event', function (evt, ctx) {
self.user = oauth.details.name;
self.token = oauth.token;
self.authenticated = true;
var name = window.prompt('who would you like to greet?');
// <5>
$http.get('/greetings-service/greet/' + name)
.success(function (greetingData) {
self.greetingFromZuulRoute = greetingData.greeting;
})
.error(function (e) {
console.log('oops!' + JSON.stringify(e));
});
// <6>
$http.get('/lets/greet/' + name)
.success(function (greetingData) {
self.greetingFromEdgeService = greetingData.greeting;
})
.error(function (e) {
console.log('oops!' + JSON.stringify(e));
});
});
});
So it is expected, that login procedure is initiated and login form appears.
Actual result: browser gets redirected to authorization server and hits CORS error
Access to XMLHttpRequest at 'http://localhost:9191/uaa/oauth/authorize?client_id=html5&redirect_uri=http://localhost:8082/login&response_type=code&state=1zegi7' (redirected from 'http://localhost:8082/user') from origin 'http://localhost:8082' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Here: localhost:8082 - Gateway service, localhost:9191 - authorization server.
In Browser console I can see that was an OPTIONS request.
On the other hand, if I explicitly call /login endpoint (provided by spring) it works as expected - the login form appears and
after credentials validation I get redirected back to the home page.
Gateway service has simple servlet filter, where I explicitly set ACCESS_CONTROL_ALLOW_ORIGIN header.
#Component
class CorsFilter implements Filter {
private final Log log = LogFactory.getLog(getClass());
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = HttpServletResponse.class.cast(res);
HttpServletRequest request = HttpServletRequest.class.cast(req);
log.info(request.getMethod());
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
The questions:
How to properly handle preflight requests in such case?
Shouldn't servlet filter handle OPTIONS request? (I do not see it in logs)
What I have tried:
Use explicit servlet filter (shown above)
Use HttpSecurity.cors() method in conjuction with CorsConfigurationSource bean:
#Configuration
#EnableOAuth2Sso
class SsoConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().antMatcher("/**").authorizeRequests()
.antMatchers( "/", "/app.js", "/login**", "/webjars/**").permitAll().anyRequest()
.authenticated().and().logout().logoutSuccessUrl("/").permitAll().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedMethods(Arrays.asList("GET","POST","OPTIONS"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
UPDATE: Here are properties I used for SSO gateway:
spring.application.name=greetings-client
server.port=${PORT:8082}
security.oauth2.resource.userInfoUri=http://auth-service/uaa/user
spring.mvc.dispatch-options-request=true
zuul.routes.hi.path=/lets/**
zuul.routes.hi.serviceId=greetings-service
management.security.enabled=false
zuul.ignoredServices=*
eureka.instance.preferIpAddress=true
eureka.instance.leaseRenewalIntervalInSeconds=10
And here are properties for Auth service:
server.port=${PORT:9191}
spring.application.name=auth-service
server.context-path=/uaa
security.sessions=if_required
logging.level.org.springframework.security=DEBUG
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=true
eureka.instance.preferIpAddress=true
eureka.instance.leaseRenewalIntervalInSeconds=10
And Authorization server configuration:
#Configuration
#EnableAuthorizationServer
class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final ClientDetailsService clientDetailsService;
#Autowired
public AuthorizationServerConfiguration(
AuthenticationManager authenticationManager,
ClientDetailsService clientDetailsService) {
this.authenticationManager = authenticationManager;
this.clientDetailsService = clientDetailsService;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// <1>
clients.withClientDetails(this.clientDetailsService);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
// <2>
endpoints.authenticationManager(this.authenticationManager);
}
}

Receiving a single line of text from servlet using XMLHttp request

I am trying to send an XMLHttp request from a JSP to a servlet and display a message in alert() received from server. But I am basically receiving the <script> <script/> part with some html tag.
Here is my jsp code:
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org /TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
<script>
var xmlHttp;
function createXMLHttpRequest() {
if (window.ActiveXObject) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
}
}
function startRequest() {
createXMLHttpRequest();
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open("post", "testServlet", true);
// var msg="Hello world";
xmlHttp.send(null);
}
function handleStateChange(){
if(xmlHttp.readyState == 4){
if(xmlHttp.status == 200){
alert("The server replied with: " +xmlHttp.responseText);
}
}
}
</script>
</head>
<body>
<form action="#">
<input type="button" value="Press ME!!"
onclick="startRequest();"/>
</body>
</html>
Here is my servlet code:
package pk1;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#WebServlet("/testServlet")
public class testServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public testServlet() {
super();
}
protected void doGet( HttpServletRequest request,
HttpServletResponse response)throws ServletException, IOException {
test(response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
test(response);
}
public void test(HttpServletResponse response) throws IOException{
response.setContentType("text/plain");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setCharacterEncoding("UTF-8");
String data = "This is a response from testServlet";
PrintWriter out=response.getWriter();
out.write(data);
out.flush();
out.close();
}
}
Since I am using Tomcat7 I guess I don't need to specify URL mapping? so what am I doing wrong here? kindly let me know.
Add this line
function startRequest() {
createXMLHttpRequest();
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open("post", "testServlet", true);
xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); // add here
xmlHttp.send(null);
And also in your servlet first try with doPost,
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.print("Hi ");
}
Hope it helps! notify if it works

servlet ajax nullpointerexception

i keep getting a null pointer exception whenever i try to read from the ajax parameters in my servlet.
heres the ajax code:
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%#page import="java.sql.*;" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script>
function findNext(id,label){
alert(id);
alert(label);
var xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState===4 && xmlhttp.status===200)
{
var ret=xmlhttp.responseText;
var prob=ret.substring(0,ret.indexOf("///"));
var pdesc=ret.substring(3);
document.getElementById("prob").innerHTML=prob;
document.getElementById("pdesc").innerHTML=pdesc;
}
};
xmlhttp.open("POST","Servlet1",true);
var sendstr="pid="+id+"&labelid="+label;
xmlhttp.send(sendstr);
}
</script>
</head>
<body>
<%
Connection con;
Statement s;
ResultSet rs;
String sql,pid=null,prob=null,pdesc=null,yesL=null,noL=null;
try{
Class.forName("com.mysql.jdbc.Driver");
con=DriverManager.getConnection("jdbc:mysql://localhost:3306/cdt", "root", ".hack%//sign66");
s=con.createStatement();
sql="select * from cdt.problem where pid='1.01'";
rs=s.executeQuery(sql);
rs.next();
pid=rs.getObject("pid").toString();
prob=rs.getObject("problm").toString();
pdesc=rs.getObject("pdesc").toString();
yesL=rs.getObject("yesL").toString();
noL=rs.getObject("noL").toString();
}
catch(Exception e){
e.printStackTrace();
}
%>
<!--STYLE THESE 2 <P> STATEMENTS-->
<p id="prob"><%=prob%></p>
<p id="pdesc"><%=pdesc%></p>
<form>
<input type="button" value="Yes" onclick="findNext(<%=pid%>,<%=yesL%>);"/>
<input type="button" value="No" onclick="findNext(<%=pid%>,<%=noL%>);"/>
</form>
</body>
</html>
the variables that i pass to the function read fine through the alert.
heres the servlet code:
package cdt1;
import cdt.FindNextLabelLocal;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ejb.EJB;
public class Servlet1 extends HttpServlet {
Connection con;
Statement s;
ResultSet rs;
#EJB private FindNextLabelLocal obj;
#Override
public void init(ServletConfig config) throws ServletException{
try{
Class.forName("com.mysql.jdbc.Driver");
con=DriverManager.getConnection("jdbc:mysql://localhost:3306/cdt", "root", ".hack%//sign66");
s=con.createStatement();
}
catch(Exception e){
e.printStackTrace();
}
}
#SuppressWarnings("UnusedAssignment")
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
String pid,label,res;
#SuppressWarnings("UnusedAssignment")
float current,next;
current=next = 0;
pid=request.getParameter("pid").toString(); // pid of the question
label=request.getParameter("labelid").toString(); //label of the next question or solution based on button press
current=Float.parseFloat(pid);
next=Float.parseFloat(label);
res=obj.findNext(current,next).toString();
out.write(res);
}
catch(Exception e){
e.printStackTrace();
}
finally {
out.close();
}
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
#Override
public String getServletInfo() {
return "Short description";
}
}
the null exception occurs java.lang.NullPointerException
at cdt1.Servlet1.processRequest(Servlet1.java:66)
at cdt1.Servlet1.doPost(Servlet1.java:124)
the 1st one occurs when i try to read the pid value
the 2nd one when processRequest is being called.
i have looked up a lot of resources on the web that show how to use ajax and servlets and as per the things i've read i am doing nothing wrong. If anyone can point me in the right direction it will be awesome. Thanks in advance. I am using netbeans 7.3 with glassfish 3.1.2 if that makes a difference
solved it. it is necessary to set the content-type http header when using post to send data.
also needed during put requests

How to handle servlet response in JSP through AJAX call?

I want to display servlet response in my JSP page (as a hyperlink) through an ajax call. Can anyone please tell me how I could display the content in my jsp page? I am also not too sure if I am doing it the right way. There could be some errors in either my servlet class or Ajax.js. I'm still in learning phase. Here is my code snippet:
JSP page
<script type="text/javascript"> var AJAX_SERVLET="<%=renderResponse.encodeURL(renderRequest.getContextPath())%>/ajaxServlet";
</script>
<label for="push">Push to start</label>
<button dojoType="dijit.form.Button" style="width: 4em" type="button" name="submitButton" value="Submit" onclick="ajaxFunction()"></button>
Ajax.js
function ajaxFunction() {
if (xmlhttp) {
xmlhttp.open("GET", AJAX_SERVLET, true); //AJAX_SERVLET has the servlet path
xmlhttp.onreadystatechange = handleServerResponse;
xmlhttp.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');
xmlhttp.send(null);
}
}
function handleServerResponse() {
if (xmlhttp.readyState == 4) {
//alert(xmlhttp.status);
if (xmlhttp.status == 200) {
var resultContent =httpRequest.getResponseHeader("Content-Type");
} else {
alert("Error during AJAX call. Please try again");
}
}
Getters/Setters
public class SearchResponse {
private String productNumber;
private String productType;
private String funcDesignation;}
Servlet Class
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
List result = new ArrayList();
result.add(new SearchResponse("001", "User Manual", "Operator"));
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(new Gson().toJson(result));
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
doPost(request, response);
}

Resources