mboost-dp1
JSF read-response og fejlbeskeder til brugeren
- Forside
- ⟨
- Forum
- ⟨
- Programmering
Hej
I forbindelse med en klassisk "login" side skal jeg udskrive en fejlbesked til brugeren hvis informationerne er forkerte.
Det er gjort med følgende test-setup
og med Login metode
Hvad jeg så ikke kan finde ud af er hvordan jeg kan aflæse "failure" værdien fra min view (login.jsp) så jeg kan vise en custom fejlbesked (ikke JSF validators, de er horrible).
Jeg skal så på linje 20 af følgende havde indsat min fejlbesked, men kan ikke se hvordan det kan gøres på en logisk smart og elegant måde.
I forbindelse med en klassisk "login" side skal jeg udskrive en fejlbesked til brugeren hvis informationerne er forkerte.
Det er gjort med følgende test-setup
<faces-config>
<managed-bean>
<managed-bean-name>Login</managed-bean-name>
<managed-bean-class>website.Login</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<navigation-rule>
<display-name>Index</display-name>
<from-view-id>/WEB-INF/index.jsp</from-view-id>
<navigation-case>
<from-action>#{SimpleLogin.Login}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/WEB-INF/index.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
og med Login metode
public String login()
{
if(username == "test" && password.equals("test"))
{
return "success";
}
return "failure";
}
Hvad jeg så ikke kan finde ud af er hvordan jeg kan aflæse "failure" værdien fra min view (login.jsp) så jeg kan vise en custom fejlbesked (ikke JSF validators, de er horrible).
Jeg skal så på linje 20 af følgende havde indsat min fejlbesked, men kan ikke se hvordan det kan gøres på en logisk smart og elegant måde.
<%@ page language="java" contentType="text/html" pageEncoding="UTF-8"
%><%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"
%><%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"
%><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<head>
<title>MyWebsite</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="media/style.css" media="all" />
</head>
<body>
<h1>MyWebsite</h1>
<f:view>
<h:form id="login">
<fieldset style="width: 280px;">
<legend>Login</legend>
<!-- Skal have en fejlbesked her, i formattet <div class="error">FEJL</div> -->
<p>
<label>Username</label>
<h:inputText styleClass="field" id="username" required="true" />
</p>
<p>
<label>Password</label>
<h:inputSecret styleClass="field" id="password" required="true" />
</p>
<p>
<h:commandButton action="#{Login.login}" value="Login" />
</p>
</fieldset>
</h:form>
</f:view>
</body>
</html>
#2
Det ideele ville jo være at definere fejlbeskeden fra serveren, ie. fra min Backed Bean.
Men som jeg ser det er en Bean aldrig klar over at der er foregået et request, og kan derfor ikke teste på responsedata.
Pointen er at jeg nægter at redirecte brugeren til en anden side bare for at skrive en fejlmeddelse. Hvilket arkitekturen ellers (desværre) lægger op til.
Desuden, hvordan finder jeg over hovedet ud af hvad jeg skal teste på?
Jeg vil jo gerne vide hvad Login.login() retunerer.
Det ideele ville jo være at definere fejlbeskeden fra serveren, ie. fra min Backed Bean.
Men som jeg ser det er en Bean aldrig klar over at der er foregået et request, og kan derfor ikke teste på responsedata.
Pointen er at jeg nægter at redirecte brugeren til en anden side bare for at skrive en fejlmeddelse. Hvilket arkitekturen ellers (desværre) lægger op til.
Desuden, hvordan finder jeg over hovedet ud af hvad jeg skal teste på?
Jeg vil jo gerne vide hvad Login.login() retunerer.
Men det kræver jeg gemmer status af Login.login() efter det er blevet kaldt?
Det virker meget underligt og ikke særlig elegant.
Altså
Login
- username: foo
- password: bar
JSF sætter Login.username og Login.password, og kalder login()
login() retunere fejl som response på et POST request.
Jeg kan så aflæse fejlværdien fra login() fra en eller anden magisk variabel der burde komme via viewstate.
Derudover undre jeg mig også meget over at jeg ikke kan mappe den til at kalde login(username,password) istedet for at skulle bruge setters før jeg kan kalde en parameterløs metode.
Jeg kan ikke rigtig se ideen i det.
Det virker meget underligt og ikke særlig elegant.
Altså
Login
- username: foo
- password: bar
JSF sætter Login.username og Login.password, og kalder login()
login() retunere fejl som response på et POST request.
Jeg kan så aflæse fejlværdien fra login() fra en eller anden magisk variabel der burde komme via viewstate.
Derudover undre jeg mig også meget over at jeg ikke kan mappe den til at kalde login(username,password) istedet for at skulle bruge setters før jeg kan kalde en parameterløs metode.
Jeg kan ikke rigtig se ideen i det.
Nogen teknisk grund, eller bare fordi at det er simpelt ? :)arne_v (8) skrev:PS: username == "test" er skidt test. :-)
Et eksempel ville være super, da jeg kun kan finde eksempler og dokumentation på hvordan man laver det med redirects til *andre* sider ved success/failure.
Jeg er langtfra nogen guru til JSF, men følgende ser ud til at virke:
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<!doctype html public "-//w3c/dtd HTML 4.01 Transitional//en">
<html>
<head>
<title>Login</title>
</head>
<body>
<c:if test='${login.res == "Failure"}'>
<c:out value='${login.msg}'/>
</c:if>
<f:view>
<h:form>
Username: <h:inputText id="un" value="#{login.un}"/>
<br>
Password: <h:inputSecret id="pw" value="#{login.pw}"/>
<br>
<h:commandButton action="#{login.login}" value="Login"/>
</h:form>
</f:view>
</html>
package test;
import java.io.Serializable;
public class Login implements Serializable {
private String un = "";
private String pw = "";
private String res = "";
private String msg = "";
public String getUn() {
return un;
}
public void setUn(String un) {
this.un = un;
}
public String getPw() {
return pw;
}
public void setPw(String pw) {
this.pw = pw;
}
public String getRes() {
return res;
}
public void setRes(String res) {
this.res = res;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String login() {
if(un.equals("test") && pw.equals("test")) {
res = "Success";
msg = "";
} else {
res = "Failure";
msg = "If you are a hacker then fuckoff !";
}
return res;
}
}
<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
<navigation-rule>
<from-view-id>/login.jsp</from-view-id>
<navigation-case>
<from-outcome>Success</from-outcome>
<to-view-id>/something.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>Failure</from-outcome>
<to-view-id>/login.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<managed-bean>
<managed-bean-name>login</managed-bean-name>
<managed-bean-class>test.Login</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>
Jeg vil da tro at du skal bruge både sessions og persistering - bare til forskellige ting.
Der er rigeligt at vælge imellem med hensyn til persistering, men hvis du er til ORM, så vil jeg mene at enten Hibernate eller JPA (og implementationen kunne såmænd godt være Hibernate) var oplagte.
Der er rigeligt at vælge imellem med hensyn til persistering, men hvis du er til ORM, så vil jeg mene at enten Hibernate eller JPA (og implementationen kunne såmænd godt være Hibernate) var oplagte.
Er er overhovedet nogen som benytter HTTP authentication i dag?arne_v (18) skrev:Med hensyn til login - er det med vilje at du vil lade din applikation checke login og ikke lade containeren checke login ?
Alt login fungerer da ligesom newz.dk, hvor man så slår op i en database ved login for at validere brugernavn og password.
Men det lader til at værre en størrere hovedpine at implementere login-check på givne undersider. Set i forhold til at jeg allerede benytter et framework på et framework, så burde det jo være lavet til at kunne klares med 2-3 linjers kode (ala. PHP).
Men container auth, som f.eks. dette her til tomcat: http://www.onjava.com/pub/a/onjava/2002/06/12/form...
Der hardcoder man jo rettighederne, og kræver en restart af sin webserver for at ændre hvem der har adgang til hvad.
Normalt ville man udvikle en løsning så at man kan definere hvilket resources som role X kan tilgå via. databasen, så man aldrig skal opdatere sin kode eller restarte serveren.
Java EE ser ud til at have trang til at gøre det anderledes end hvad der er standard for alle andre web-teknologier.
Der hardcoder man jo rettighederne, og kræver en restart af sin webserver for at ændre hvem der har adgang til hvad.
Normalt ville man udvikle en løsning så at man kan definere hvilket resources som role X kan tilgå via. databasen, så man aldrig skal opdatere sin kode eller restarte serveren.
Java EE ser ud til at have trang til at gøre det anderledes end hvad der er standard for alle andre web-teknologier.
#23
Ja - det er den korte version af container managed auth.
Og jeg forstår ikke helt din kritik.
I web.xml angiver du hvilke roller der har adgang til hvilke resourcer.
Via et eller andet tool styrer du hvilke brugere der har hvilke roller.
Saa du beskytter admin delen af din app så kun brugere med administrator rolle kan tilgå den. Og giver bruger windcape den rolle.
Jeg har lidt svært ved at se behovet for dynamisk at have mulighed for at fjerne administrator rollens adgang til admin delen og i.s.f. give den til guest rollen.
Rollerne har normalt tilknyttet semantik.
Og jeg mener heller ikke at det er anderledes end i ASP.NET, så vidt jeg kan se så er det:
Java EE
--------
web.xml fragment:
<security-constraint>
<web-resource-collection>
<web-resource-name>some name</web-resource-name>
<url-pattern>/somepath</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>somerole</role-name>
</auth-constraint>
</security-constraint>
ASP.NET
---------
web.config fragment:
<location path="somepath">
<system.web>
<authorization>
<allow roles="somerole"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
Præcis det samme.
Ja - det er den korte version af container managed auth.
Og jeg forstår ikke helt din kritik.
I web.xml angiver du hvilke roller der har adgang til hvilke resourcer.
Via et eller andet tool styrer du hvilke brugere der har hvilke roller.
Saa du beskytter admin delen af din app så kun brugere med administrator rolle kan tilgå den. Og giver bruger windcape den rolle.
Jeg har lidt svært ved at se behovet for dynamisk at have mulighed for at fjerne administrator rollens adgang til admin delen og i.s.f. give den til guest rollen.
Rollerne har normalt tilknyttet semantik.
Og jeg mener heller ikke at det er anderledes end i ASP.NET, så vidt jeg kan se så er det:
Java EE
--------
web.xml fragment:
<security-constraint>
<web-resource-collection>
<web-resource-name>some name</web-resource-name>
<url-pattern>/somepath</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>somerole</role-name>
</auth-constraint>
</security-constraint>
ASP.NET
---------
web.config fragment:
<location path="somepath">
<system.web>
<authorization>
<allow roles="somerole"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
Præcis det samme.
Men begge metoder kræver genstart (og evt. rekompilering) for at tilføje / fjerne roller, og tilføje / fjerne paths de kan tilgå.
Derudover låser man sig jo til een specific container hvis man laver den slags auth.
F.eks. i ASP.NET har jeg set apps som brugte IIS definerede users, altså næsten byggede det sammen med styresystemet.
Så ryger hele argumentet for at det ikke er låst til et miljø, hvilket jo er en af de eneste grunde til at vælge java i dag, som jeg ser det.
Men må nok indrømme at teknologien ihvertfald ikke gør det nemt at lave login anderledes. Men selv på den måde synes jeg det er ydest forvirrende at tildele roller til brugere, uden at skulle til at sætte databasen direkte i forbindelse med containeren (tomcat).
Det minder om en gammeldags fjollet måde at lave http auth på, der "bare" supportere forms nu, fordi at det er mere brugervenligt.
Derudover låser man sig jo til een specific container hvis man laver den slags auth.
F.eks. i ASP.NET har jeg set apps som brugte IIS definerede users, altså næsten byggede det sammen med styresystemet.
Så ryger hele argumentet for at det ikke er låst til et miljø, hvilket jo er en af de eneste grunde til at vælge java i dag, som jeg ser det.
Men må nok indrømme at teknologien ihvertfald ikke gør det nemt at lave login anderledes. Men selv på den måde synes jeg det er ydest forvirrende at tildele roller til brugere, uden at skulle til at sætte databasen direkte i forbindelse med containeren (tomcat).
Det minder om en gammeldags fjollet måde at lave http auth på, der "bare" supportere forms nu, fordi at det er mere brugervenligt.
Løsningen med et filter
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
HttpSession session = ((HttpServletRequest)request).getSession();
Authentication auth = (Authentication)session.getAttribute(Authentication.class.getSimpleName());
if((auth == null) || (!auth.isAuthenticated()))
{
((HttpServletResponse)response).sendRedirect("../login.jsp");
}
else
{
chain.doFilter(request, response);
}
}
SUN er vel entligt lidt fjollede.
session.getAttribute(string arg0);
Istedet for at benytte string, skulle de da have benyttet et strong-typed classname for ordenlig refractoring.
Da vi diskutterede det i den anden tråd var netop refractoring en af de vigtige punkter.
Jeg valgte så at benytte MyClass.class.getSimpleName() men det virker bare fjollet set i forhold til f.eks. C#: typeof(MyClass).
(Og hvis jeg ser rigtigt bruges der slet ikke generics i JSF overhovedet?)
session.getAttribute(string arg0);
Istedet for at benytte string, skulle de da have benyttet et strong-typed classname for ordenlig refractoring.
Da vi diskutterede det i den anden tråd var netop refractoring en af de vigtige punkter.
Jeg valgte så at benytte MyClass.class.getSimpleName() men det virker bare fjollet set i forhold til f.eks. C#: typeof(MyClass).
(Og hvis jeg ser rigtigt bruges der slet ikke generics i JSF overhovedet?)
#25
Det er korrekt at en ændring kræver en reload af app. Sådan er det bare.
Man låser sig ikke til en bestemt container. Syntaxen i web.xml (og kaldene i servlet API hvis man vil mixe den declarative security med lidt programmatic security) er helt standard, så din web app vil virke uændret i alle containere.
Opsætningen af bruger og rolle "databasen" er container specifik.
Tomcat kommer f.eks. med backends til:
- database via JDBC
- database via DataSource via JNDI
- LDAP via JNDI
- XML file
- JAAS
og derudover er det ret nemt at lave sin egen backend (nem som i 50 linier kode).
JBoss understøtter:
- properties file
- LDAP
- database
- certifikater
og det er også relativt nemt at lave sin egen.
Det er korrekt at en ændring kræver en reload af app. Sådan er det bare.
Man låser sig ikke til en bestemt container. Syntaxen i web.xml (og kaldene i servlet API hvis man vil mixe den declarative security med lidt programmatic security) er helt standard, så din web app vil virke uændret i alle containere.
Opsætningen af bruger og rolle "databasen" er container specifik.
Tomcat kommer f.eks. med backends til:
- database via JDBC
- database via DataSource via JNDI
- LDAP via JNDI
- XML file
- JAAS
og derudover er det ret nemt at lave sin egen backend (nem som i 50 linier kode).
JBoss understøtter:
- properties file
- LDAP
- database
- certifikater
og det er også relativt nemt at lave sin egen.
Af ren og skær interesse, hvordan kan man lave en muteable key, hvis keys er en stærk type (MyClass.class i java).arne_v (29) skrev:Men man undgår problemer med mutable keys på den måde.
Så vidt jeg kan se kan den kun ændre sig hvis man ændre navnet på klassen, hvilket jo alligevel vil kræve at man ændre sin string key.
Eller er det for at undgå 2 klasser med samme navn, i forskellige packages? Så at hvis man havde brugt en forkert (autoimports og typo's sker jo), så ville ens refractoring opdatere navnet forkert?
Eller noget helt tredje? :)
#30
.setAttribute(Object key, Object value) og .getAttribute(Object key) vil tillade brug af mutable keys, hvilket kan gå grueligt galt.
.setAttribute(Class key, Object value) og .getAttribute(Class key) vil ikke tillade mutable keys, men jeg kan ikke rigtigt se nogen pointe i at bruge klasser som identifikation.
.setAttribute(Object key, Object value) og .getAttribute(Object key) vil tillade brug af mutable keys, hvilket kan gå grueligt galt.
.setAttribute(Class key, Object value) og .getAttribute(Class key) vil ikke tillade mutable keys, men jeg kan ikke rigtigt se nogen pointe i at bruge klasser som identifikation.
#34
Jeg er helt enig i at session.getAttribute(MyClass.class.getName()) er bedre end session.getAtttribute("mypackage.MyClass"), men hvorfor bruge klassenavnet ?
session.getAttribute("foobar") eller session.getAttribute(SesionKeys.FOOBAR) synes at give lidt mere fleksibilitet med hensyn til valg af et beskrivende navn fremfor at lægge sigt fast på et klasse navn.
Jeg er helt enig i at session.getAttribute(MyClass.class.getName()) er bedre end session.getAtttribute("mypackage.MyClass"), men hvorfor bruge klassenavnet ?
session.getAttribute("foobar") eller session.getAttribute(SesionKeys.FOOBAR) synes at give lidt mere fleksibilitet med hensyn til valg af et beskrivende navn fremfor at lægge sigt fast på et klasse navn.
Oh, det var nok fordi at i dette her tilfælde tænkte jeg udelukkende på Managed Beans. Havde glemt at det også kunne benyttes til alm. sessions
<managed-bean>
<managed-bean-name>Authentication</managed-bean-name>
<managed-bean-class>website.Authentication</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Gå til top
Opret dig som bruger i dag
Det er gratis, og du binder dig ikke til noget.
Når du er oprettet som bruger, får du adgang til en lang række af sidens andre muligheder, såsom at udforme siden efter eget ønske og deltage i diskussionerne.