Java for Web学习笔记(六九):Service和Repository(4)Principal

来源:互联网 发布:python写淘宝秒杀脚本 编辑:程序博客网 时间:2024/06/05 02:57

什么是Principal

java.security.Principal,顾名思义和安全有关,但目前我们仅用其最最基本的含义,以后会进一步学习。

JAAS所使用的认证方案以两种非常重要的实体为基础:principal和subject。实际被认证的人或者服务称为subject。principal是一个惟一的实体,比如个人或者组的名字、帐号、社会安全号或者类似的惟一标识。为了惟一标识一个subject(这是认证的关键部分),一个或者多个principal必须与这个subject相关联。[1]

HttpServletRequest提供getUserPrincipal()来获取,其返回值通过HttpSerlvetRequestWrapper来设定。

在小例子中,我们将存放在session中的username放在请求的principal中,方便获取。

自定义所需的Principal

小例子中,用户的关键信息就是用户名。Pincipal是个接口,需要实现getName()。为了方便比对两个principal是否相同(本例就是用户名,而不是对象地址),重写了Object类的equals(为此重写了hashcode),我们还允许clone。
public class UserPrincipal implements Principal, Cloneable, Serializable{    private static final long serialVersionUID = 1L; //这是Serializable要求给出,小例子不存在传输和读写在其他介质,可以不用Serializable。    //【1】小例子中最重要的身份信息是username,该信息一次性填入,不允许修改    private final String username;     public UserPrincipal(String username) {        this.username = username;    }    //【2】getName()是Principal接口,是主要使用的方法    @Override    public String getName() {        return this.username;    }    //【3】hashCode()和equals()即是Principal接口也是Object的接口,我们比对两个principal是否一直,不是比较对象的地址,而是里面的信息。同时修改toString()给出显示的信息    @Override    public int hashCode() {        return this.username.hashCode();    }    @Override    public boolean equals(Object obj) {        return obj instanceof UserPrincipal                 && ((UserPrincipal) obj).username.equals(this.username);    }    @Override    public String toString() {        return this.username;    }     //【4】允许colon    @Override    protected UserPrincipal clone() throws CloneNotSupportedException {        return (UserPrincipal)super.clone();    }    //【5】提供两个静态方法getPrincipal()和setPrincipal(),将principal和session中的某个属性对应起来,也就是真实的仍借用session进行数据存放。放置在session,session过期后,将不存在        public static Principal getPrincipal(HttpSession session){        return session == null ? null :                (Principal)session.getAttribute("cn.wei.flowingflying.customer_support.user.principal");    }    public static void setPrincipal(HttpSession session, Principal principal){        session.setAttribute("cn.wei.flowingflying.customer_support.user.principal", principal);    }}

小例子:用principal存放应用信息并进行auth

设置认证服务接口:AuthenticationService

public interface AuthenticationService {    /** 如果认证成功,返回principal对象,失败,返回null */    Principal authenticate(String username, String password);}

设置认证控制器:login相关的Controller

@Controllerpublic class AuthenticationController {    @Inject private AuthenticationService authenticationService;    @RequestMapping(value="login",method=RequestMethod.GET)    public ModelAndView login(Map<String,Object> model,HttpSession session){        //【1】如果存在principal,说明已经登录了,进入主界面。通过filter的处理(见紧接),我们也可以通过request.getPrincipal()来获取该值。        if(UserPrincipal.getPrincipal(session) != null)            return getHome();        model.put("loginFailed", false);        model.put("loginForm",new Form());        return new ModelAndView("login");    }    @RequestMapping(value = "login", method = RequestMethod.POST)    public ModelAndView login(Map<String,Object> model,HttpSession session,HttpServletRequest request,Form form){        //【2】进行身份校验,获取principal        Principal principal = this.authenticationService.authenticate(form.getUsername(), form.getPassword());        // 2.1)如果校验失败        if(principal == null){            logger.warn("Login failed for user {}",form.getUsername());            form.setPassword(null);            model.put("loginFailed", false);            model.put("loginForm", form);            return new ModelAndView("login");        }        // 2.2)如果校验成功,设置principal        UserPrincipal.setPrincipal(session, principal);        request.changeSessionId();                return getHome();    }    ......}

允许request.getUserPrincipal()获取:AuthenticationFilter

AuthenticationFilter有两个作用:

  1. 我们应对每个HTTP请求(除了静态资源,如图片,css文件)进行用户身份认证检查,采用Filter的方式。
  2. 封装request,支持request.getUserPrincipal()操作。
public class AuthenticationFilter implements Filter {    ... ...    public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain) throws IOException,ServletException{        //【1】获取principal        HttpSession session = ((HttpServletRequest)request).getSession(false);                final Principal principal = UserPrincipal.getPrincipal(session);        //【2】如果principal为null(未登录),进入登录界面        if(principal == null){             ((HttpServletResponse)response).sendRedirect(                      ((HttpServletRequest)request).getContextPath() + "/login");        }else{         //【3】如果principal存在(已登录),我们希望能从request中直接获取principal信息。通过wrapper request,给出getUserPrincipal()的返回            chain.doFilter(                new HttpServletRequestWrapper((HttpServletRequest)request){                    @Override                    public Principal getUserPrincipal() {                        return principal;                    }                },response);        }    } }

在LoggingFilter中使用principal

我们为了让log4j2在log中%X{username}给出登录用户名,写了LoggingFilter,在里面通过principal获取。在BootStrap中LoggingFilter是位于AuthenticationFilter之前,所有采用request.getUserPrincipal()没有效果,需要通过UserPrincipal的静态方法来获取。

public class LoggingFilter implements Filter {    ... ...    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)                          throws IOException, ServletException {        // 如果LoggingFilter在AuthenticationFilter之前先执行,则无法使用request.getPrincipal()的处理。然而我们提供了从session中获取的静态方法,可以方便获得        Principal principal = UserPrincipal.getPrincipal(((HttpServletRequest)request).getSession(false));        if(principal != null){            ThreadContext.put("username", principal.getName());        }        try{            chain.doFilter(request, response);        }finally{                    ThreadContext.clearAll();       }    }}

相关链接: 我的Professional Java for Web Applications相关文章

阅读全文
0 0
原创粉丝点击